提交 726b8548 编写于 作者: Q Quinn Tran 提交者: Nicholas Bellinger

qla2xxx: Add framework for async fabric discovery

Currently code performs a full scan of the fabric for
every RSCN. Its an expensive process in a noisy large SAN.

This patch optimizes expensive fabric discovery process by
scanning switch for the affected port when RSCN is received.

Currently Initiator Mode code makes login/logout decision without
knowledge of target mode. This causes driver and firmware to go
out-of-sync. This framework synchronizes both initiator mode
personality and target mode personality in making login/logout
decision.

This patch adds following capabilities in the driver

- Send Notification Acknowledgement asynchronously.
- Update session/fcport state asynchronously.
- Create a session or fcport struct asynchronously.
- Send GNL asynchronously. The command will ask FW to
  provide a list of FC Port entries FW knows about.
- Send GPDB asynchronously. The command will ask FW to
  provide detail data of an FC Port FW knows about or
  perform ADISC to verify the state of the session.
- Send GPNID asynchronously. The command will ask switch
  to provide WWPN for provided NPort ID.
- Send GPSC asynchronously. The command will ask switch
  to provide registered port speed for provided WWPN.
- Send GIDPN asynchronously. The command will ask the
  switch to provide Nport ID for provided WWPN.
- In driver unload path, schedule all session for deletion
  and wait for deletion to complete before allowing driver
  unload to proceed.
Signed-off-by: NQuinn Tran <quinn.tran@cavium.com>
Signed-off-by: NHimanshu Madhani <himanshu.madhani@cavium.com>
[ bvanassche: fixed spelling in patch description ]
Signed-off-by: NBart Van Assche <bart.vanassche@sandisk.com>
Signed-off-by: NNicholas Bellinger <nab@linux-iscsi.org>
上级 5d964837
...@@ -2163,6 +2163,9 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) ...@@ -2163,6 +2163,9 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
clear_bit(vha->vp_idx, ha->vp_idx_map); clear_bit(vha->vp_idx, ha->vp_idx_map);
mutex_unlock(&ha->vport_lock); mutex_unlock(&ha->vport_lock);
dma_free_coherent(&ha->pdev->dev, vha->gnl.size, vha->gnl.l,
vha->gnl.ldma);
if (vha->qpair->vp_idx == vha->vp_idx) { if (vha->qpair->vp_idx == vha->vp_idx) {
if (qla2xxx_delete_qpair(vha, vha->qpair) != QLA_SUCCESS) if (qla2xxx_delete_qpair(vha, vha->qpair) != QLA_SUCCESS)
ql_log(ql_log_warn, vha, 0x7087, ql_log(ql_log_warn, vha, 0x7087,
......
...@@ -55,6 +55,8 @@ ...@@ -55,6 +55,8 @@
#include "qla_settings.h" #include "qla_settings.h"
#define MODE_DUAL (MODE_TARGET | MODE_INITIATOR)
/* /*
* Data bit definitions * Data bit definitions
*/ */
...@@ -251,6 +253,14 @@ ...@@ -251,6 +253,14 @@
#define MAX_CMDSZ 16 /* SCSI maximum CDB size. */ #define MAX_CMDSZ 16 /* SCSI maximum CDB size. */
#include "qla_fw.h" #include "qla_fw.h"
struct name_list_extended {
struct get_name_list_extended *l;
dma_addr_t ldma;
struct list_head fcports; /* protect by sess_list */
u32 size;
u8 sent;
};
/* /*
* Timeout timer counts in seconds * Timeout timer counts in seconds
*/ */
...@@ -309,6 +319,17 @@ struct els_logo_payload { ...@@ -309,6 +319,17 @@ struct els_logo_payload {
uint8_t wwpn[WWN_SIZE]; uint8_t wwpn[WWN_SIZE];
}; };
struct ct_arg {
void *iocb;
u16 nport_handle;
dma_addr_t req_dma;
dma_addr_t rsp_dma;
u32 req_size;
u32 rsp_size;
void *req;
void *rsp;
};
/* /*
* SRB extensions. * SRB extensions.
*/ */
...@@ -320,6 +341,7 @@ struct srb_iocb { ...@@ -320,6 +341,7 @@ struct srb_iocb {
#define SRB_LOGIN_COND_PLOGI BIT_1 #define SRB_LOGIN_COND_PLOGI BIT_1
#define SRB_LOGIN_SKIP_PRLI BIT_2 #define SRB_LOGIN_SKIP_PRLI BIT_2
uint16_t data[2]; uint16_t data[2];
u32 iop[2];
} logio; } logio;
struct { struct {
#define ELS_DCMD_TIMEOUT 20 #define ELS_DCMD_TIMEOUT 20
...@@ -372,6 +394,16 @@ struct srb_iocb { ...@@ -372,6 +394,16 @@ struct srb_iocb {
__le16 comp_status; __le16 comp_status;
struct completion comp; struct completion comp;
} abt; } abt;
struct ct_arg ctarg;
struct {
__le16 in_mb[28]; /* fr fw */
__le16 out_mb[28]; /* to fw */
void *out, *in;
dma_addr_t out_dma, in_dma;
} mbx;
struct {
struct imm_ntfy_from_isp *ntfy;
} nack;
} u; } u;
struct timer_list timer; struct timer_list timer;
...@@ -392,16 +424,24 @@ struct srb_iocb { ...@@ -392,16 +424,24 @@ struct srb_iocb {
#define SRB_FXIOCB_BCMD 11 #define SRB_FXIOCB_BCMD 11
#define SRB_ABT_CMD 12 #define SRB_ABT_CMD 12
#define SRB_ELS_DCMD 13 #define SRB_ELS_DCMD 13
#define SRB_MB_IOCB 14
#define SRB_CT_PTHRU_CMD 15
#define SRB_NACK_PLOGI 16
#define SRB_NACK_PRLI 17
#define SRB_NACK_LOGO 18
typedef struct srb { typedef struct srb {
atomic_t ref_count; atomic_t ref_count;
struct fc_port *fcport; struct fc_port *fcport;
void *vha;
uint32_t handle; uint32_t handle;
uint16_t flags; uint16_t flags;
uint16_t type; uint16_t type;
char *name; char *name;
int iocbs; int iocbs;
struct qla_qpair *qpair; struct qla_qpair *qpair;
u32 gen1; /* scratch */
u32 gen2; /* scratch */
union { union {
struct srb_iocb iocb_cmd; struct srb_iocb iocb_cmd;
struct bsg_job *bsg_job; struct bsg_job *bsg_job;
...@@ -2101,6 +2141,18 @@ typedef struct { ...@@ -2101,6 +2141,18 @@ typedef struct {
#define FC4_TYPE_OTHER 0x0 #define FC4_TYPE_OTHER 0x0
#define FC4_TYPE_UNKNOWN 0xff #define FC4_TYPE_UNKNOWN 0xff
/* mailbox command 4G & above */
struct mbx_24xx_entry {
uint8_t entry_type;
uint8_t entry_count;
uint8_t sys_define1;
uint8_t entry_status;
uint32_t handle;
uint16_t mb[28];
};
#define IOCB_SIZE 64
/* /*
* Fibre channel port type. * Fibre channel port type.
*/ */
...@@ -2113,6 +2165,12 @@ typedef enum { ...@@ -2113,6 +2165,12 @@ typedef enum {
FCT_TARGET FCT_TARGET
} fc_port_type_t; } fc_port_type_t;
enum qla_sess_deletion {
QLA_SESS_DELETION_NONE = 0,
QLA_SESS_DELETION_IN_PROGRESS,
QLA_SESS_DELETED,
};
enum qlt_plogi_link_t { enum qlt_plogi_link_t {
QLT_PLOGI_LINK_SAME_WWN, QLT_PLOGI_LINK_SAME_WWN,
QLT_PLOGI_LINK_CONFLICT, QLT_PLOGI_LINK_CONFLICT,
...@@ -2124,6 +2182,48 @@ struct qlt_plogi_ack_t { ...@@ -2124,6 +2182,48 @@ struct qlt_plogi_ack_t {
struct imm_ntfy_from_isp iocb; struct imm_ntfy_from_isp iocb;
port_id_t id; port_id_t id;
int ref_count; int ref_count;
void *fcport;
};
struct ct_sns_desc {
struct ct_sns_pkt *ct_sns;
dma_addr_t ct_sns_dma;
};
enum discovery_state {
DSC_DELETED,
DSC_GID_PN,
DSC_GNL,
DSC_LOGIN_PEND,
DSC_LOGIN_FAILED,
DSC_GPDB,
DSC_GPSC,
DSC_UPD_FCPORT,
DSC_LOGIN_COMPLETE,
DSC_DELETE_PEND,
};
enum login_state { /* FW control Target side */
DSC_LS_LLIOCB_SENT = 2,
DSC_LS_PLOGI_PEND,
DSC_LS_PLOGI_COMP,
DSC_LS_PRLI_PEND,
DSC_LS_PRLI_COMP,
DSC_LS_PORT_UNAVAIL,
DSC_LS_PRLO_PEND = 9,
DSC_LS_LOGO_PEND,
};
enum fcport_mgt_event {
FCME_RELOGIN = 1,
FCME_RSCN,
FCME_GIDPN_DONE,
FCME_PLOGI_DONE, /* Initiator side sent LLIOCB */
FCME_GNL_DONE,
FCME_GPSC_DONE,
FCME_GPDB_DONE,
FCME_GPNID_DONE,
FCME_DELETE_DONE,
}; };
/* /*
...@@ -2143,9 +2243,13 @@ typedef struct fc_port { ...@@ -2143,9 +2243,13 @@ typedef struct fc_port {
unsigned int deleted:2; unsigned int deleted:2;
unsigned int local:1; unsigned int local:1;
unsigned int logout_on_delete:1; unsigned int logout_on_delete:1;
unsigned int logo_ack_needed:1;
unsigned int keep_nport_handle:1; unsigned int keep_nport_handle:1;
unsigned int send_els_logo:1; unsigned int send_els_logo:1;
unsigned int login_pause:1;
unsigned int login_succ:1;
struct fc_port *conflict;
unsigned char logout_completed; unsigned char logout_completed;
int generation; int generation;
...@@ -2186,8 +2290,30 @@ typedef struct fc_port { ...@@ -2186,8 +2290,30 @@ typedef struct fc_port {
unsigned long retry_delay_timestamp; unsigned long retry_delay_timestamp;
struct qla_tgt_sess *tgt_session; struct qla_tgt_sess *tgt_session;
struct ct_sns_desc ct_desc;
enum discovery_state disc_state;
enum login_state fw_login_state;
u32 login_gen, last_login_gen;
u32 rscn_gen, last_rscn_gen;
u32 chip_reset;
struct list_head gnl_entry;
struct work_struct del_work;
u8 iocb[IOCB_SIZE];
} fc_port_t; } fc_port_t;
#define QLA_FCPORT_SCAN 1
#define QLA_FCPORT_FOUND 2
struct event_arg {
enum fcport_mgt_event event;
fc_port_t *fcport;
srb_t *sp;
port_id_t id;
u16 data[2], rc;
u8 port_name[WWN_SIZE];
u32 iop[2];
};
#include "qla_mr.h" #include "qla_mr.h"
/* /*
...@@ -2265,6 +2391,10 @@ static const char * const port_state_str[] = { ...@@ -2265,6 +2391,10 @@ static const char * const port_state_str[] = {
#define GFT_ID_REQ_SIZE (16 + 4) #define GFT_ID_REQ_SIZE (16 + 4)
#define GFT_ID_RSP_SIZE (16 + 32) #define GFT_ID_RSP_SIZE (16 + 32)
#define GID_PN_CMD 0x121
#define GID_PN_REQ_SIZE (16 + 8)
#define GID_PN_RSP_SIZE (16 + 4)
#define RFT_ID_CMD 0x217 #define RFT_ID_CMD 0x217
#define RFT_ID_REQ_SIZE (16 + 4 + 32) #define RFT_ID_REQ_SIZE (16 + 4 + 32)
#define RFT_ID_RSP_SIZE 16 #define RFT_ID_RSP_SIZE 16
...@@ -2590,6 +2720,10 @@ struct ct_sns_req { ...@@ -2590,6 +2720,10 @@ struct ct_sns_req {
uint8_t reserved; uint8_t reserved;
uint8_t port_name[3]; uint8_t port_name[3];
} gff_id; } gff_id;
struct {
uint8_t port_name[8];
} gid_pn;
} req; } req;
}; };
...@@ -2669,6 +2803,10 @@ struct ct_sns_rsp { ...@@ -2669,6 +2803,10 @@ struct ct_sns_rsp {
struct { struct {
uint8_t fc4_features[128]; uint8_t fc4_features[128];
} gff_id; } gff_id;
struct {
uint8_t reserved;
uint8_t port_id[3];
} gid_pn;
} rsp; } rsp;
}; };
...@@ -2810,11 +2948,11 @@ struct isp_operations { ...@@ -2810,11 +2948,11 @@ struct isp_operations {
uint16_t (*calc_req_entries) (uint16_t); uint16_t (*calc_req_entries) (uint16_t);
void (*build_iocbs) (srb_t *, cmd_entry_t *, uint16_t); void (*build_iocbs) (srb_t *, cmd_entry_t *, uint16_t);
void * (*prep_ms_iocb) (struct scsi_qla_host *, uint32_t, uint32_t); void *(*prep_ms_iocb) (struct scsi_qla_host *, struct ct_arg *);
void * (*prep_ms_fdmi_iocb) (struct scsi_qla_host *, uint32_t, void *(*prep_ms_fdmi_iocb) (struct scsi_qla_host *, uint32_t,
uint32_t); uint32_t);
uint8_t * (*read_nvram) (struct scsi_qla_host *, uint8_t *, uint8_t *(*read_nvram) (struct scsi_qla_host *, uint8_t *,
uint32_t, uint32_t); uint32_t, uint32_t);
int (*write_nvram) (struct scsi_qla_host *, uint8_t *, uint32_t, int (*write_nvram) (struct scsi_qla_host *, uint8_t *, uint32_t,
uint32_t); uint32_t);
...@@ -2876,13 +3014,21 @@ enum qla_work_type { ...@@ -2876,13 +3014,21 @@ enum qla_work_type {
QLA_EVT_AEN, QLA_EVT_AEN,
QLA_EVT_IDC_ACK, QLA_EVT_IDC_ACK,
QLA_EVT_ASYNC_LOGIN, QLA_EVT_ASYNC_LOGIN,
QLA_EVT_ASYNC_LOGIN_DONE,
QLA_EVT_ASYNC_LOGOUT, QLA_EVT_ASYNC_LOGOUT,
QLA_EVT_ASYNC_LOGOUT_DONE, QLA_EVT_ASYNC_LOGOUT_DONE,
QLA_EVT_ASYNC_ADISC, QLA_EVT_ASYNC_ADISC,
QLA_EVT_ASYNC_ADISC_DONE, QLA_EVT_ASYNC_ADISC_DONE,
QLA_EVT_UEVENT, QLA_EVT_UEVENT,
QLA_EVT_AENFX, QLA_EVT_AENFX,
QLA_EVT_GIDPN,
QLA_EVT_GPNID,
QLA_EVT_GPNID_DONE,
QLA_EVT_NEW_SESS,
QLA_EVT_GPDB,
QLA_EVT_GPSC,
QLA_EVT_UPD_FCPORT,
QLA_EVT_GNL,
QLA_EVT_NACK,
}; };
...@@ -2918,6 +3064,23 @@ struct qla_work_evt { ...@@ -2918,6 +3064,23 @@ struct qla_work_evt {
struct { struct {
srb_t *sp; srb_t *sp;
} iosb; } iosb;
struct {
port_id_t id;
} gpnid;
struct {
port_id_t id;
u8 port_name[8];
void *pla;
} new_sess;
struct { /*Get PDB, Get Speed, update fcport, gnl, gidpn */
fc_port_t *fcport;
u8 opt;
} fcport;
struct {
fc_port_t *fcport;
u8 iocb[IOCB_SIZE];
int type;
} nack;
} u; } u;
}; };
...@@ -3899,6 +4062,10 @@ typedef struct scsi_qla_host { ...@@ -3899,6 +4062,10 @@ typedef struct scsi_qla_host {
struct qla8044_reset_template reset_tmplt; struct qla8044_reset_template reset_tmplt;
struct qla_tgt_counters tgt_counters; struct qla_tgt_counters tgt_counters;
uint16_t bbcr; uint16_t bbcr;
struct name_list_extended gnl;
/* Count of active session/fcport */
int fcport_count;
wait_queue_head_t fcport_waitQ;
} scsi_qla_host_t; } scsi_qla_host_t;
struct qla27xx_image_status { struct qla27xx_image_status {
......
...@@ -72,6 +72,37 @@ struct port_database_24xx { ...@@ -72,6 +72,37 @@ struct port_database_24xx {
uint8_t reserved_3[24]; uint8_t reserved_3[24];
}; };
/*
* MB 75h returns a list of DB entries similar to port_database_24xx(64B).
* However, in this case it returns 1st 40 bytes.
*/
struct get_name_list_extended {
__le16 flags;
u8 current_login_state;
u8 last_login_state;
u8 hard_address[3];
u8 reserved_1;
u8 port_id[3];
u8 sequence_id;
__le16 port_timer;
__le16 nport_handle; /* N_PORT handle. */
__le16 receive_data_size;
__le16 reserved_2;
/* PRLI SVC Param are Big endian */
u8 prli_svc_param_word_0[2]; /* Bits 15-0 of word 0 */
u8 prli_svc_param_word_3[2]; /* Bits 15-0 of word 3 */
u8 port_name[WWN_SIZE];
u8 node_name[WWN_SIZE];
};
/* MB 75h: This is the short version of the database */
struct get_name_list {
u8 port_node_name[WWN_SIZE]; /* B7 most sig, B0 least sig */
__le16 nport_handle;
u8 reserved;
};
struct vp_database_24xx { struct vp_database_24xx {
uint16_t vp_status; uint16_t vp_status;
uint8_t options; uint8_t options;
......
...@@ -73,6 +73,10 @@ extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *, ...@@ -73,6 +73,10 @@ extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *,
uint16_t *); uint16_t *);
extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *, extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *,
uint16_t *); uint16_t *);
struct qla_work_evt *qla2x00_alloc_work(struct scsi_qla_host *,
enum qla_work_type);
extern int qla24xx_async_gnl(struct scsi_qla_host *, fc_port_t *);
int qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e);
extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *); extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *);
extern void *qla2x00_alloc_iocbs_ready(struct scsi_qla_host *, srb_t *); extern void *qla2x00_alloc_iocbs_ready(struct scsi_qla_host *, srb_t *);
extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *); extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *);
...@@ -94,6 +98,13 @@ extern uint8_t qla27xx_find_valid_image(struct scsi_qla_host *); ...@@ -94,6 +98,13 @@ extern uint8_t qla27xx_find_valid_image(struct scsi_qla_host *);
extern struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *, extern struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *,
int, int); int, int);
extern int qla2xxx_delete_qpair(struct scsi_qla_host *, struct qla_qpair *); extern int qla2xxx_delete_qpair(struct scsi_qla_host *, struct qla_qpair *);
void qla2x00_fcport_event_handler(scsi_qla_host_t *, struct event_arg *);
int qla24xx_async_gpdb(struct scsi_qla_host *, fc_port_t *, u8);
int qla24xx_async_notify_ack(scsi_qla_host_t *, fc_port_t *,
struct imm_ntfy_from_isp *, int);
int qla24xx_post_newsess_work(struct scsi_qla_host *, port_id_t *, u8 *,
void *);
int qla24xx_fcport_handle_login(struct scsi_qla_host *, fc_port_t *);
/* /*
* Global Data in qla_os.c source file. * Global Data in qla_os.c source file.
...@@ -135,8 +146,6 @@ extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum ...@@ -135,8 +146,6 @@ extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum
extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *); extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *);
extern int qla2x00_post_async_login_work(struct scsi_qla_host *, fc_port_t *, extern int qla2x00_post_async_login_work(struct scsi_qla_host *, fc_port_t *,
uint16_t *); uint16_t *);
extern int qla2x00_post_async_login_done_work(struct scsi_qla_host *,
fc_port_t *, uint16_t *);
extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *, extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
uint16_t *); uint16_t *);
extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *, extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
...@@ -179,6 +188,10 @@ extern void qla2x00_disable_board_on_pci_error(struct work_struct *); ...@@ -179,6 +188,10 @@ extern void qla2x00_disable_board_on_pci_error(struct work_struct *);
extern void qla2x00_sp_compl(void *, void *, int); extern void qla2x00_sp_compl(void *, void *, int);
extern void qla2xxx_qpair_sp_free_dma(void *, void *); extern void qla2xxx_qpair_sp_free_dma(void *, void *);
extern void qla2xxx_qpair_sp_compl(void *, void *, int); extern void qla2xxx_qpair_sp_compl(void *, void *, int);
extern int qla24xx_post_upd_fcport_work(struct scsi_qla_host *, fc_port_t *);
void qla2x00_handle_login_done_event(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
int qla24xx_post_gnl_work(struct scsi_qla_host *, fc_port_t *);
/* /*
* Global Functions in qla_mid.c source file. * Global Functions in qla_mid.c source file.
...@@ -301,9 +314,6 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *); ...@@ -301,9 +314,6 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
extern int extern int
qla2x00_init_firmware(scsi_qla_host_t *, uint16_t); qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
extern int
qla2x00_get_node_name_list(scsi_qla_host_t *, void **, int *);
extern int extern int
qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t); qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
...@@ -483,6 +493,9 @@ qla2x00_process_completed_request(struct scsi_qla_host *, struct req_que *, ...@@ -483,6 +493,9 @@ qla2x00_process_completed_request(struct scsi_qla_host *, struct req_que *,
uint32_t); uint32_t);
extern irqreturn_t extern irqreturn_t
qla2xxx_msix_rsp_q(int irq, void *dev_id); qla2xxx_msix_rsp_q(int irq, void *dev_id);
fc_port_t *qla2x00_find_fcport_by_loopid(scsi_qla_host_t *, uint16_t);
fc_port_t *qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *, u8 *, u8);
fc_port_t *qla2x00_find_fcport_by_nportid(scsi_qla_host_t *, port_id_t *, u8);
/* /*
* Global Function Prototypes in qla_sup.c source file. * Global Function Prototypes in qla_sup.c source file.
...@@ -574,8 +587,8 @@ extern void qla2xxx_dump_post_process(scsi_qla_host_t *, int); ...@@ -574,8 +587,8 @@ extern void qla2xxx_dump_post_process(scsi_qla_host_t *, int);
/* /*
* Global Function Prototypes in qla_gs.c source file. * Global Function Prototypes in qla_gs.c source file.
*/ */
extern void *qla2x00_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t); extern void *qla2x00_prep_ms_iocb(scsi_qla_host_t *, struct ct_arg *);
extern void *qla24xx_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t); extern void *qla24xx_prep_ms_iocb(scsi_qla_host_t *, struct ct_arg *);
extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *); extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *);
extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *);
extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *);
...@@ -591,6 +604,23 @@ extern int qla2x00_fdmi_register(scsi_qla_host_t *); ...@@ -591,6 +604,23 @@ extern int qla2x00_fdmi_register(scsi_qla_host_t *);
extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *);
extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *);
extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *, size_t); extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *, size_t);
extern int qla2x00_chk_ms_status(scsi_qla_host_t *, ms_iocb_entry_t *,
struct ct_sns_rsp *, const char *);
extern void qla2x00_async_iocb_timeout(void *data);
extern int qla24xx_async_gidpn(scsi_qla_host_t *, fc_port_t *);
int qla24xx_post_gidpn_work(struct scsi_qla_host *, fc_port_t *);
void qla24xx_handle_gidpn_event(scsi_qla_host_t *, struct event_arg *);
extern void qla2x00_free_fcport(fc_port_t *);
extern int qla24xx_post_gpnid_work(struct scsi_qla_host *, port_id_t *);
extern int qla24xx_async_gpnid(scsi_qla_host_t *, port_id_t *);
void qla24xx_async_gpnid_done(scsi_qla_host_t *, srb_t*);
void qla24xx_handle_gpnid_event(scsi_qla_host_t *, struct event_arg *);
int qla24xx_post_gpsc_work(struct scsi_qla_host *, fc_port_t *);
int qla24xx_async_gpsc(scsi_qla_host_t *, fc_port_t *);
int qla2x00_mgmt_svr_login(scsi_qla_host_t *);
/* /*
* Global Function Prototypes in qla_attr.c source file. * Global Function Prototypes in qla_attr.c source file.
...@@ -803,4 +833,16 @@ extern int qla_get_exchoffld_status(scsi_qla_host_t *, uint16_t *, uint16_t *); ...@@ -803,4 +833,16 @@ extern int qla_get_exchoffld_status(scsi_qla_host_t *, uint16_t *, uint16_t *);
extern int qla_set_exchoffld_mem_cfg(scsi_qla_host_t *, dma_addr_t); extern int qla_set_exchoffld_mem_cfg(scsi_qla_host_t *, dma_addr_t);
extern void qlt_handle_abts_recv(struct scsi_qla_host *, response_t *); extern void qlt_handle_abts_recv(struct scsi_qla_host *, response_t *);
int qla24xx_async_notify_ack(scsi_qla_host_t *, fc_port_t *,
struct imm_ntfy_from_isp *, int);
void qla24xx_do_nack_work(struct scsi_qla_host *, struct qla_work_evt *);
void qlt_plogi_ack_link(struct scsi_qla_host *, struct qlt_plogi_ack_t *,
struct fc_port *, enum qlt_plogi_link_t);
void qlt_plogi_ack_unref(struct scsi_qla_host *, struct qlt_plogi_ack_t *);
extern void qlt_schedule_sess_for_deletion(struct fc_port *, bool);
extern void qlt_schedule_sess_for_deletion_lock(struct fc_port *);
extern struct fc_port *qlt_find_sess_invalidate_other(scsi_qla_host_t *,
uint64_t wwn, port_id_t port_id, uint16_t loop_id, struct fc_port **);
void qla24xx_delete_sess_fn(struct work_struct *);
#endif /* _QLA_GBL_H */ #endif /* _QLA_GBL_H */
...@@ -24,12 +24,12 @@ static int qla2x00_sns_rnn_id(scsi_qla_host_t *); ...@@ -24,12 +24,12 @@ static int qla2x00_sns_rnn_id(scsi_qla_host_t *);
* Returns a pointer to the @ha's ms_iocb. * Returns a pointer to the @ha's ms_iocb.
*/ */
void * void *
qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
{ {
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
ms_pkt = ha->ms_iocb; ms_pkt = (ms_iocb_entry_t *)arg->iocb;
memset(ms_pkt, 0, sizeof(ms_iocb_entry_t)); memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
ms_pkt->entry_type = MS_IOCB_TYPE; ms_pkt->entry_type = MS_IOCB_TYPE;
...@@ -39,15 +39,15 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ...@@ -39,15 +39,15 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ms_pkt->cmd_dsd_count = cpu_to_le16(1); ms_pkt->cmd_dsd_count = cpu_to_le16(1);
ms_pkt->total_dsd_count = cpu_to_le16(2); ms_pkt->total_dsd_count = cpu_to_le16(2);
ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); ms_pkt->rsp_bytecount = cpu_to_le32(arg->rsp_size);
ms_pkt->req_bytecount = cpu_to_le32(req_size); ms_pkt->req_bytecount = cpu_to_le32(arg->req_size);
ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(arg->req_dma));
ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(arg->req_dma));
ms_pkt->dseg_req_length = ms_pkt->req_bytecount; ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(arg->rsp_dma));
ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(arg->rsp_dma));
ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount; ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount;
vha->qla_stats.control_requests++; vha->qla_stats.control_requests++;
...@@ -64,29 +64,29 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ...@@ -64,29 +64,29 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
* Returns a pointer to the @ha's ms_iocb. * Returns a pointer to the @ha's ms_iocb.
*/ */
void * void *
qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
{ {
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct ct_entry_24xx *ct_pkt; struct ct_entry_24xx *ct_pkt;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; ct_pkt = (struct ct_entry_24xx *)arg->iocb;
memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_type = CT_IOCB_TYPE;
ct_pkt->entry_count = 1; ct_pkt->entry_count = 1;
ct_pkt->nport_handle = cpu_to_le16(NPH_SNS); ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ct_pkt->cmd_dsd_count = cpu_to_le16(1); ct_pkt->cmd_dsd_count = cpu_to_le16(1);
ct_pkt->rsp_dsd_count = cpu_to_le16(1); ct_pkt->rsp_dsd_count = cpu_to_le16(1);
ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
ct_pkt->cmd_byte_count = cpu_to_le32(req_size); ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(arg->req_dma));
ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(arg->req_dma));
ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count; ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count;
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(arg->rsp_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(arg->rsp_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
ct_pkt->vp_index = vha->vp_idx; ct_pkt->vp_index = vha->vp_idx;
...@@ -117,7 +117,7 @@ qla2x00_prep_ct_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size) ...@@ -117,7 +117,7 @@ qla2x00_prep_ct_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size)
return &p->p.req; return &p->p.req;
} }
static int int
qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
struct ct_sns_rsp *ct_rsp, const char *routine) struct ct_sns_rsp *ct_rsp, const char *routine)
{ {
...@@ -183,14 +183,21 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -183,14 +183,21 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_ga_nxt(vha, fcport); return qla2x00_sns_ga_nxt(vha, fcport);
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GA_NXT_REQ_SIZE;
arg.rsp_size = GA_NXT_RSP_SIZE;
arg.nport_handle = NPH_SNS;
/* Issue GA_NXT */ /* Issue GA_NXT */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GA_NXT_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
GA_NXT_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, GA_NXT_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, GA_NXT_CMD,
...@@ -269,16 +276,24 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -269,16 +276,24 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
struct ct_sns_gid_pt_data *gid_data; struct ct_sns_gid_pt_data *gid_data;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
uint16_t gid_pt_rsp_size; uint16_t gid_pt_rsp_size;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_gid_pt(vha, list); return qla2x00_sns_gid_pt(vha, list);
gid_data = NULL; gid_data = NULL;
gid_pt_rsp_size = qla2x00_gid_pt_rsp_size(vha); gid_pt_rsp_size = qla2x00_gid_pt_rsp_size(vha);
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GID_PT_REQ_SIZE;
arg.rsp_size = gid_pt_rsp_size;
arg.nport_handle = NPH_SNS;
/* Issue GID_PT */ /* Issue GID_PT */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
gid_pt_rsp_size);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, GID_PT_CMD, gid_pt_rsp_size); ct_req = qla2x00_prep_ct_req(ha->ct_sns, GID_PT_CMD, gid_pt_rsp_size);
...@@ -344,15 +359,22 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -344,15 +359,22 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_gpn_id(vha, list); return qla2x00_sns_gpn_id(vha, list);
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GPN_ID_REQ_SIZE;
arg.rsp_size = GPN_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
for (i = 0; i < ha->max_fibre_devices; i++) { for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GPN_ID */ /* Issue GPN_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
GPN_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, GPN_ID_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, GPN_ID_CMD,
...@@ -406,15 +428,22 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -406,15 +428,22 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_gnn_id(vha, list); return qla2x00_sns_gnn_id(vha, list);
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GNN_ID_REQ_SIZE;
arg.rsp_size = GNN_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
for (i = 0; i < ha->max_fibre_devices; i++) { for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GNN_ID */ /* Issue GNN_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
GNN_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, GNN_ID_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, GNN_ID_CMD,
...@@ -473,14 +502,21 @@ qla2x00_rft_id(scsi_qla_host_t *vha) ...@@ -473,14 +502,21 @@ qla2x00_rft_id(scsi_qla_host_t *vha)
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_rft_id(vha); return qla2x00_sns_rft_id(vha);
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = RFT_ID_REQ_SIZE;
arg.rsp_size = RFT_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
/* Issue RFT_ID */ /* Issue RFT_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFT_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
RFT_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD,
...@@ -526,6 +562,7 @@ qla2x00_rff_id(scsi_qla_host_t *vha) ...@@ -526,6 +562,7 @@ qla2x00_rff_id(scsi_qla_host_t *vha)
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) { if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
ql_dbg(ql_dbg_disc, vha, 0x2046, ql_dbg(ql_dbg_disc, vha, 0x2046,
...@@ -533,10 +570,16 @@ qla2x00_rff_id(scsi_qla_host_t *vha) ...@@ -533,10 +570,16 @@ qla2x00_rff_id(scsi_qla_host_t *vha)
return (QLA_SUCCESS); return (QLA_SUCCESS);
} }
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = RFF_ID_REQ_SIZE;
arg.rsp_size = RFF_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
/* Issue RFF_ID */ /* Issue RFF_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFF_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
RFF_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD,
...@@ -584,14 +627,21 @@ qla2x00_rnn_id(scsi_qla_host_t *vha) ...@@ -584,14 +627,21 @@ qla2x00_rnn_id(scsi_qla_host_t *vha)
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_rnn_id(vha); return qla2x00_sns_rnn_id(vha);
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = RNN_ID_REQ_SIZE;
arg.rsp_size = RNN_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
/* Issue RNN_ID */ /* Issue RNN_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RNN_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
RNN_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE); ct_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE);
...@@ -651,6 +701,7 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) ...@@ -651,6 +701,7 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha)
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) { if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
ql_dbg(ql_dbg_disc, vha, 0x2050, ql_dbg(ql_dbg_disc, vha, 0x2050,
...@@ -658,10 +709,17 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) ...@@ -658,10 +709,17 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha)
return (QLA_SUCCESS); return (QLA_SUCCESS);
} }
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = 0;
arg.rsp_size = RSNN_NN_RSP_SIZE;
arg.nport_handle = NPH_SNS;
/* Issue RSNN_NN */ /* Issue RSNN_NN */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */ /* Request size adjusted after CT preparation */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE); ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD,
...@@ -1103,7 +1161,7 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha) ...@@ -1103,7 +1161,7 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha)
* *
* Returns 0 on success. * Returns 0 on success.
*/ */
static int int
qla2x00_mgmt_svr_login(scsi_qla_host_t *vha) qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
{ {
int ret, rval; int ret, rval;
...@@ -2425,15 +2483,22 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2425,15 +2483,22 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (!IS_IIDMA_CAPABLE(ha)) if (!IS_IIDMA_CAPABLE(ha))
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GFPN_ID_REQ_SIZE;
arg.rsp_size = GFPN_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
for (i = 0; i < ha->max_fibre_devices; i++) { for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GFPN_ID */ /* Issue GFPN_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
GFPN_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFPN_ID_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFPN_ID_CMD,
...@@ -2471,36 +2536,6 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2471,36 +2536,6 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
return (rval); return (rval);
} }
static inline void *
qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
struct ct_entry_24xx *ct_pkt;
struct qla_hw_data *ha = vha->hw;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
ct_pkt->entry_type = CT_IOCB_TYPE;
ct_pkt->entry_count = 1;
ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ct_pkt->cmd_dsd_count = cpu_to_le16(1);
ct_pkt->rsp_dsd_count = cpu_to_le16(1);
ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count;
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
ct_pkt->vp_index = vha->vp_idx;
return ct_pkt;
}
static inline struct ct_sns_req * static inline struct ct_sns_req *
qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd, qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd,
...@@ -2530,9 +2565,10 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2530,9 +2565,10 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
int rval; int rval;
uint16_t i; uint16_t i;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt; ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req; struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct ct_arg arg;
if (!IS_IIDMA_CAPABLE(ha)) if (!IS_IIDMA_CAPABLE(ha))
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
...@@ -2543,11 +2579,17 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2543,11 +2579,17 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
if (rval) if (rval)
return rval; return rval;
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GPSC_REQ_SIZE;
arg.rsp_size = GPSC_RSP_SIZE;
arg.nport_handle = vha->mgmt_svr_loop_id;
for (i = 0; i < ha->max_fibre_devices; i++) { for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GFPN_ID */ /* Issue GFPN_ID */
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE, ms_pkt = qla24xx_prep_ms_iocb(vha, &arg);
GPSC_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla24xx_prep_ct_fm_req(ha->ct_sns, GPSC_CMD, ct_req = qla24xx_prep_ct_fm_req(ha->ct_sns, GPSC_CMD,
...@@ -2641,6 +2683,7 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2641,6 +2683,7 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list)
struct ct_sns_rsp *ct_rsp; struct ct_sns_rsp *ct_rsp;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
uint8_t fcp_scsi_features = 0; uint8_t fcp_scsi_features = 0;
struct ct_arg arg;
for (i = 0; i < ha->max_fibre_devices; i++) { for (i = 0; i < ha->max_fibre_devices; i++) {
/* Set default FC4 Type as UNKNOWN so the default is to /* Set default FC4 Type as UNKNOWN so the default is to
...@@ -2651,9 +2694,15 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2651,9 +2694,15 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list)
if (!IS_FWI2_CAPABLE(ha)) if (!IS_FWI2_CAPABLE(ha))
continue; continue;
arg.iocb = ha->ms_iocb;
arg.req_dma = ha->ct_sns_dma;
arg.rsp_dma = ha->ct_sns_dma;
arg.req_size = GFF_ID_REQ_SIZE;
arg.rsp_size = GFF_ID_RSP_SIZE;
arg.nport_handle = NPH_SNS;
/* Prepare common MS IOCB */ /* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFF_ID_REQ_SIZE, ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg);
GFF_ID_RSP_SIZE);
/* Prepare CT request */ /* Prepare CT request */
ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFF_ID_CMD, ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFF_ID_CMD,
...@@ -2692,3 +2741,536 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) ...@@ -2692,3 +2741,536 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list)
break; break;
} }
} }
/* GID_PN completion processing. */
void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea)
{
fc_port_t *fcport = ea->fcport;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC login state %d \n",
__func__, fcport->port_name, fcport->fw_login_state);
if (ea->sp->gen2 != fcport->login_gen) {
/* PLOGI/PRLI/LOGO came in while cmd was out.*/
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC generation changed rscn %d|%d login %d|%d \n",
__func__, fcport->port_name, fcport->last_rscn_gen,
fcport->rscn_gen, fcport->last_login_gen, fcport->login_gen);
return;
}
if (!ea->rc) {
if (ea->sp->gen1 == fcport->rscn_gen) {
fcport->scan_state = QLA_FCPORT_FOUND;
fcport->flags |= FCF_FABRIC_DEVICE;
if (fcport->d_id.b24 == ea->id.b24) {
/* cable plugged into the same place */
switch (vha->host->active_mode) {
case MODE_TARGET:
/* NOOP. let the other guy login to us.*/
break;
case MODE_INITIATOR:
case MODE_DUAL:
default:
if (atomic_read(&fcport->state) ==
FCS_ONLINE)
break;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gnl\n",
__func__, __LINE__, fcport->port_name);
qla24xx_post_gnl_work(vha, fcport);
break;
}
} else { /* fcport->d_id.b24 != ea->id.b24 */
fcport->d_id.b24 = ea->id.b24;
if (fcport->deleted == QLA_SESS_DELETED) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__, fcport->port_name);
qlt_schedule_sess_for_deletion_lock(fcport);
}
}
} else { /* ea->sp->gen1 != fcport->rscn_gen */
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gidpn\n",
__func__, __LINE__, fcport->port_name);
/* rscn came in while cmd was out */
qla24xx_post_gidpn_work(vha, fcport);
}
} else { /* ea->rc */
/* cable pulled */
if (ea->sp->gen1 == fcport->rscn_gen) {
if (ea->sp->gen2 == fcport->login_gen) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n", __func__,
__LINE__, fcport->port_name);
qlt_schedule_sess_for_deletion_lock(fcport);
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC login\n", __func__, __LINE__,
fcport->port_name);
qla24xx_fcport_handle_login(vha, fcport);
}
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gidpn\n", __func__, __LINE__,
fcport->port_name);
qla24xx_post_gidpn_work(vha, fcport);
}
}
} /* gidpn_event */
static void qla2x00_async_gidpn_sp_done(void *v, void *s, int res)
{
struct scsi_qla_host *vha = (struct scsi_qla_host *)v;
struct srb *sp = (struct srb *)s;
fc_port_t *fcport = sp->fcport;
u8 *id = fcport->ct_desc.ct_sns->p.rsp.rsp.gid_pn.port_id;
struct event_arg ea;
fcport->flags &= ~FCF_ASYNC_SENT;
memset(&ea, 0, sizeof(ea));
ea.fcport = fcport;
ea.id.b.domain = id[0];
ea.id.b.area = id[1];
ea.id.b.al_pa = id[2];
ea.sp = sp;
ea.rc = res;
ea.event = FCME_GIDPN_DONE;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async done-%s res %x, WWPN %8phC ID %3phC \n",
sp->name, res, fcport->port_name, id);
qla2x00_fcport_event_handler(vha, &ea);
sp->free(vha, sp);
}
int qla24xx_async_gidpn(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval = QLA_FUNCTION_FAILED;
struct ct_sns_req *ct_req;
srb_t *sp;
if (!vha->flags.online)
goto done;
fcport->flags |= FCF_ASYNC_SENT;
fcport->disc_state = DSC_GID_PN;
fcport->scan_state = QLA_FCPORT_SCAN;
sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
if (!sp)
goto done;
sp->type = SRB_CT_PTHRU_CMD;
sp->name = "gidpn";
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
/* CT_IU preamble */
ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GID_PN_CMD,
GID_PN_RSP_SIZE);
/* GIDPN req */
memcpy(ct_req->req.gid_pn.port_name, fcport->port_name,
WWN_SIZE);
/* req & rsp use the same buffer */
sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns;
sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma;
sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns;
sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma;
sp->u.iocb_cmd.u.ctarg.req_size = GID_PN_REQ_SIZE;
sp->u.iocb_cmd.u.ctarg.rsp_size = GID_PN_RSP_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_gidpn_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0x206f,
"Async-%s - %8phC hdl=%x loopid=%x portid %02x%02x%02x.\n",
sp->name, fcport->port_name,
sp->handle, fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa);
return rval;
done_free_sp:
sp->free(vha, sp);
done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval;
}
int qla24xx_post_gidpn_work(struct scsi_qla_host *vha, fc_port_t *fcport)
{
struct qla_work_evt *e;
if ((atomic_read(&vha->loop_state) != LOOP_READY) ||
test_bit(UNLOADING, &vha->dpc_flags))
return 0;
e = qla2x00_alloc_work(vha, QLA_EVT_GIDPN);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.fcport.fcport = fcport;
return qla2x00_post_work(vha, e);
}
int qla24xx_post_gpsc_work(struct scsi_qla_host *vha, fc_port_t *fcport)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_GPSC);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.fcport.fcport = fcport;
return qla2x00_post_work(vha, e);
}
static void qla24xx_async_gpsc_sp_done(void *v, void *s, int res)
{
struct scsi_qla_host *vha = (struct scsi_qla_host *)v;
struct qla_hw_data *ha = vha->hw;
struct srb *sp = (struct srb *)s;
fc_port_t *fcport = sp->fcport;
struct ct_sns_rsp *ct_rsp;
struct event_arg ea;
ct_rsp = &fcport->ct_desc.ct_sns->p.rsp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async done-%s res %x, WWPN %8phC \n",
sp->name, res, fcport->port_name);
fcport->flags &= ~FCF_ASYNC_SENT;
if (res == (DID_ERROR << 16)) {
/* entry status error */
goto done;
} else if (res) {
if ((ct_rsp->header.reason_code ==
CT_REASON_INVALID_COMMAND_CODE) ||
(ct_rsp->header.reason_code ==
CT_REASON_COMMAND_UNSUPPORTED)) {
ql_dbg(ql_dbg_disc, vha, 0x205a,
"GPSC command unsupported, disabling "
"query.\n");
ha->flags.gpsc_supported = 0;
res = QLA_SUCCESS;
}
} else {
switch (be16_to_cpu(ct_rsp->rsp.gpsc.speed)) {
case BIT_15:
fcport->fp_speed = PORT_SPEED_1GB;
break;
case BIT_14:
fcport->fp_speed = PORT_SPEED_2GB;
break;
case BIT_13:
fcport->fp_speed = PORT_SPEED_4GB;
break;
case BIT_12:
fcport->fp_speed = PORT_SPEED_10GB;
break;
case BIT_11:
fcport->fp_speed = PORT_SPEED_8GB;
break;
case BIT_10:
fcport->fp_speed = PORT_SPEED_16GB;
break;
case BIT_8:
fcport->fp_speed = PORT_SPEED_32GB;
break;
}
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s OUT WWPN %8phC speeds=%04x speed=%04x.\n",
sp->name,
fcport->fabric_port_name,
be16_to_cpu(ct_rsp->rsp.gpsc.speeds),
be16_to_cpu(ct_rsp->rsp.gpsc.speed));
}
done:
memset(&ea, 0, sizeof(ea));
ea.event = FCME_GPSC_DONE;
ea.rc = res;
ea.fcport = fcport;
qla2x00_fcport_event_handler(vha, &ea);
sp->free(vha, sp);
}
int qla24xx_async_gpsc(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval = QLA_FUNCTION_FAILED;
struct ct_sns_req *ct_req;
srb_t *sp;
if (!vha->flags.online)
goto done;
fcport->flags |= FCF_ASYNC_SENT;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
sp->type = SRB_CT_PTHRU_CMD;
sp->name = "gpsc";
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
/* CT_IU preamble */
ct_req = qla24xx_prep_ct_fm_req(fcport->ct_desc.ct_sns, GPSC_CMD,
GPSC_RSP_SIZE);
/* GPSC req */
memcpy(ct_req->req.gpsc.port_name, fcport->port_name,
WWN_SIZE);
sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns;
sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma;
sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns;
sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma;
sp->u.iocb_cmd.u.ctarg.req_size = GPSC_REQ_SIZE;
sp->u.iocb_cmd.u.ctarg.rsp_size = GPSC_RSP_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = vha->mgmt_svr_loop_id;
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla24xx_async_gpsc_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s %8phC hdl=%x loopid=%x portid=%02x%02x%02x.\n",
sp->name, fcport->port_name, sp->handle,
fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa);
return rval;
done_free_sp:
sp->free(vha, sp);
done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval;
}
int qla24xx_post_gpnid_work(struct scsi_qla_host *vha, port_id_t *id)
{
struct qla_work_evt *e;
if (test_bit(UNLOADING, &vha->dpc_flags))
return 0;
e = qla2x00_alloc_work(vha, QLA_EVT_GPNID);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.gpnid.id = *id;
return qla2x00_post_work(vha, e);
}
void qla24xx_async_gpnid_done(scsi_qla_host_t *vha, srb_t *sp)
{
if (sp->u.iocb_cmd.u.ctarg.req) {
dma_free_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt),
sp->u.iocb_cmd.u.ctarg.req,
sp->u.iocb_cmd.u.ctarg.req_dma);
sp->u.iocb_cmd.u.ctarg.req = NULL;
}
if (sp->u.iocb_cmd.u.ctarg.rsp) {
dma_free_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt),
sp->u.iocb_cmd.u.ctarg.rsp,
sp->u.iocb_cmd.u.ctarg.rsp_dma);
sp->u.iocb_cmd.u.ctarg.rsp = NULL;
}
sp->free(vha, sp);
}
void qla24xx_handle_gpnid_event(scsi_qla_host_t *vha, struct event_arg *ea)
{
fc_port_t *fcport;
unsigned long flags;
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
fcport = qla2x00_find_fcport_by_wwpn(vha, ea->port_name, 1);
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
if (fcport) {
/* cable moved. just plugged in */
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__, fcport->port_name);
fcport->rscn_gen++;
fcport->d_id = ea->id;
fcport->scan_state = QLA_FCPORT_FOUND;
fcport->flags |= FCF_FABRIC_DEVICE;
qlt_schedule_sess_for_deletion_lock(fcport);
} else {
/* create new fcport */
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post new sess\n",
__func__, __LINE__, ea->port_name);
qla24xx_post_newsess_work(vha, &ea->id, ea->port_name, NULL);
}
}
static void qla2x00_async_gpnid_sp_done(void *v, void *s, int res)
{
struct scsi_qla_host *vha = (struct scsi_qla_host *)v;
struct srb *sp = (struct srb *)s;
struct ct_sns_req *ct_req =
(struct ct_sns_req *)sp->u.iocb_cmd.u.ctarg.req;
struct ct_sns_rsp *ct_rsp =
(struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp;
struct event_arg ea;
struct qla_work_evt *e;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async done-%s res %x ID %3phC. %8phC\n",
sp->name, res, ct_req->req.port_id.port_id,
ct_rsp->rsp.gpn_id.port_name);
memset(&ea, 0, sizeof(ea));
memcpy(ea.port_name, ct_rsp->rsp.gpn_id.port_name, WWN_SIZE);
ea.sp = sp;
ea.id.b.domain = ct_req->req.port_id.port_id[0];
ea.id.b.area = ct_req->req.port_id.port_id[1];
ea.id.b.al_pa = ct_req->req.port_id.port_id[2];
ea.rc = res;
ea.event = FCME_GPNID_DONE;
qla2x00_fcport_event_handler(vha, &ea);
e = qla2x00_alloc_work(vha, QLA_EVT_GPNID_DONE);
if (!e) {
/* please ignore kernel warning. otherwise, we have mem leak. */
if (sp->u.iocb_cmd.u.ctarg.req) {
dma_free_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt),
sp->u.iocb_cmd.u.ctarg.req,
sp->u.iocb_cmd.u.ctarg.req_dma);
sp->u.iocb_cmd.u.ctarg.req = NULL;
}
if (sp->u.iocb_cmd.u.ctarg.rsp) {
dma_free_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt),
sp->u.iocb_cmd.u.ctarg.rsp,
sp->u.iocb_cmd.u.ctarg.rsp_dma);
sp->u.iocb_cmd.u.ctarg.rsp = NULL;
}
sp->free(vha, sp);
return;
}
e->u.iosb.sp = sp;
qla2x00_post_work(vha, e);
}
/* Get WWPN with Nport ID. */
int qla24xx_async_gpnid(scsi_qla_host_t *vha, port_id_t *id)
{
int rval = QLA_FUNCTION_FAILED;
struct ct_sns_req *ct_req;
srb_t *sp;
struct ct_sns_pkt *ct_sns;
if (!vha->flags.online)
goto done;
sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
if (!sp)
goto done;
sp->type = SRB_CT_PTHRU_CMD;
sp->name = "gpnid";
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
GFP_KERNEL);
if (!sp->u.iocb_cmd.u.ctarg.req) {
ql_log(ql_log_warn, vha, 0xffff,
"Failed to allocate ct_sns request.\n");
goto done_free_sp;
}
sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.rsp_dma,
GFP_KERNEL);
if (!sp->u.iocb_cmd.u.ctarg.rsp) {
ql_log(ql_log_warn, vha, 0xffff,
"Failed to allocate ct_sns request.\n");
goto done_free_sp;
}
ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp;
memset(ct_sns, 0, sizeof(*ct_sns));
ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
/* CT_IU preamble */
ct_req = qla2x00_prep_ct_req(ct_sns, GPN_ID_CMD, GPN_ID_RSP_SIZE);
/* GPN_ID req */
ct_req->req.port_id.port_id[0] = id->b.domain;
ct_req->req.port_id.port_id[1] = id->b.area;
ct_req->req.port_id.port_id[2] = id->b.al_pa;
sp->u.iocb_cmd.u.ctarg.req_size = GPN_ID_REQ_SIZE;
sp->u.iocb_cmd.u.ctarg.rsp_size = GPN_ID_RSP_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_gpnid_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s hdl=%x ID %3phC.\n", sp->name,
sp->handle, ct_req->req.port_id.port_id);
return rval;
done_free_sp:
if (sp->u.iocb_cmd.u.ctarg.req) {
dma_free_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt),
sp->u.iocb_cmd.u.ctarg.req,
sp->u.iocb_cmd.u.ctarg.req_dma);
sp->u.iocb_cmd.u.ctarg.req = NULL;
}
if (sp->u.iocb_cmd.u.ctarg.rsp) {
dma_free_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt),
sp->u.iocb_cmd.u.ctarg.rsp,
sp->u.iocb_cmd.u.ctarg.rsp_dma);
sp->u.iocb_cmd.u.ctarg.rsp = NULL;
}
sp->free(vha, sp);
done:
return rval;
}
...@@ -30,15 +30,15 @@ static int qla2x00_configure_hba(scsi_qla_host_t *); ...@@ -30,15 +30,15 @@ static int qla2x00_configure_hba(scsi_qla_host_t *);
static int qla2x00_configure_loop(scsi_qla_host_t *); static int qla2x00_configure_loop(scsi_qla_host_t *);
static int qla2x00_configure_local_loop(scsi_qla_host_t *); static int qla2x00_configure_local_loop(scsi_qla_host_t *);
static int qla2x00_configure_fabric(scsi_qla_host_t *); static int qla2x00_configure_fabric(scsi_qla_host_t *);
static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *); static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *);
static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
uint16_t *);
static int qla2x00_restart_isp(scsi_qla_host_t *); static int qla2x00_restart_isp(scsi_qla_host_t *);
static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *); static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
static int qla84xx_init_chip(scsi_qla_host_t *); static int qla84xx_init_chip(scsi_qla_host_t *);
static int qla25xx_init_queues(struct qla_hw_data *); static int qla25xx_init_queues(struct qla_hw_data *);
static int qla24xx_post_gpdb_work(struct scsi_qla_host *, fc_port_t *, u8);
static void qla24xx_handle_plogi_done_event(struct scsi_qla_host *,
struct event_arg *);
/* SRB Extensions ---------------------------------------------------------- */ /* SRB Extensions ---------------------------------------------------------- */
...@@ -47,8 +47,8 @@ qla2x00_sp_timeout(unsigned long __data) ...@@ -47,8 +47,8 @@ qla2x00_sp_timeout(unsigned long __data)
{ {
srb_t *sp = (srb_t *)__data; srb_t *sp = (srb_t *)__data;
struct srb_iocb *iocb; struct srb_iocb *iocb;
fc_port_t *fcport = sp->fcport; scsi_qla_host_t *vha = (scsi_qla_host_t *)sp->vha;
struct qla_hw_data *ha = fcport->vha->hw; struct qla_hw_data *ha = vha->hw;
struct req_que *req; struct req_que *req;
unsigned long flags; unsigned long flags;
...@@ -57,7 +57,7 @@ qla2x00_sp_timeout(unsigned long __data) ...@@ -57,7 +57,7 @@ qla2x00_sp_timeout(unsigned long __data)
req->outstanding_cmds[sp->handle] = NULL; req->outstanding_cmds[sp->handle] = NULL;
iocb = &sp->u.iocb_cmd; iocb = &sp->u.iocb_cmd;
iocb->timeout(sp); iocb->timeout(sp);
sp->free(fcport->vha, sp); sp->free(vha, sp);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
} }
...@@ -94,29 +94,44 @@ qla2x00_get_async_timeout(struct scsi_qla_host *vha) ...@@ -94,29 +94,44 @@ qla2x00_get_async_timeout(struct scsi_qla_host *vha)
return tmo; return tmo;
} }
static void void
qla2x00_async_iocb_timeout(void *data) qla2x00_async_iocb_timeout(void *data)
{ {
srb_t *sp = (srb_t *)data; srb_t *sp = (srb_t *)data;
fc_port_t *fcport = sp->fcport; fc_port_t *fcport = sp->fcport;
struct srb_iocb *lio = &sp->u.iocb_cmd;
struct event_arg ea;
ql_dbg(ql_dbg_disc, fcport->vha, 0x2071, ql_dbg(ql_dbg_disc, fcport->vha, 0x2071,
"Async-%s timeout - hdl=%x portid=%02x%02x%02x.\n", "Async-%s timeout - hdl=%x portid=%06x %8phC.\n",
sp->name, sp->handle, fcport->d_id.b.domain, fcport->d_id.b.area, sp->name, sp->handle, fcport->d_id.b24, fcport->port_name);
fcport->d_id.b.al_pa);
fcport->flags &= ~FCF_ASYNC_SENT; fcport->flags &= ~FCF_ASYNC_SENT;
if (sp->type == SRB_LOGIN_CMD) {
struct srb_iocb *lio = &sp->u.iocb_cmd; switch (sp->type) {
qla2x00_post_async_logout_work(fcport->vha, fcport, NULL); case SRB_LOGIN_CMD:
/* Retry as needed. */ /* Retry as needed. */
lio->u.logio.data[0] = MBS_COMMAND_ERROR; lio->u.logio.data[0] = MBS_COMMAND_ERROR;
lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
QLA_LOGIO_LOGIN_RETRIED : 0; QLA_LOGIO_LOGIN_RETRIED : 0;
qla2x00_post_async_login_done_work(fcport->vha, fcport, memset(&ea, 0, sizeof(ea));
lio->u.logio.data); ea.event = FCME_PLOGI_DONE;
} else if (sp->type == SRB_LOGOUT_CMD) { ea.fcport = sp->fcport;
ea.data[0] = lio->u.logio.data[0];
ea.data[1] = lio->u.logio.data[1];
ea.sp = sp;
qla24xx_handle_plogi_done_event(fcport->vha, &ea);
break;
case SRB_LOGOUT_CMD:
qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT); qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT);
break;
case SRB_CT_PTHRU_CMD:
case SRB_MB_IOCB:
case SRB_NACK_PLOGI:
case SRB_NACK_PRLI:
case SRB_NACK_LOGO:
sp->done(sp->vha, sp, QLA_FUNCTION_TIMEOUT);
break;
} }
} }
...@@ -126,10 +141,25 @@ qla2x00_async_login_sp_done(void *data, void *ptr, int res) ...@@ -126,10 +141,25 @@ qla2x00_async_login_sp_done(void *data, void *ptr, int res)
srb_t *sp = (srb_t *)ptr; srb_t *sp = (srb_t *)ptr;
struct srb_iocb *lio = &sp->u.iocb_cmd; struct srb_iocb *lio = &sp->u.iocb_cmd;
struct scsi_qla_host *vha = (scsi_qla_host_t *)data; struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
struct event_arg ea;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC res %d \n",
__func__, sp->fcport->port_name, res);
sp->fcport->flags &= ~FCF_ASYNC_SENT;
if (!test_bit(UNLOADING, &vha->dpc_flags)) {
memset(&ea, 0, sizeof(ea));
ea.event = FCME_PLOGI_DONE;
ea.fcport = sp->fcport;
ea.data[0] = lio->u.logio.data[0];
ea.data[1] = lio->u.logio.data[1];
ea.iop[0] = lio->u.logio.iop[0];
ea.iop[1] = lio->u.logio.iop[1];
ea.sp = sp;
qla2x00_fcport_event_handler(vha, &ea);
}
if (!test_bit(UNLOADING, &vha->dpc_flags))
qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport,
lio->u.logio.data);
sp->free(sp->fcport->vha, sp); sp->free(sp->fcport->vha, sp);
} }
...@@ -139,13 +169,23 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -139,13 +169,23 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
{ {
srb_t *sp; srb_t *sp;
struct srb_iocb *lio; struct srb_iocb *lio;
int rval; int rval = QLA_FUNCTION_FAILED;
if (!vha->flags.online)
goto done;
if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) ||
(fcport->fw_login_state == DSC_LS_PLOGI_COMP) ||
(fcport->fw_login_state == DSC_LS_PRLI_PEND))
goto done;
rval = QLA_FUNCTION_FAILED;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp) if (!sp)
goto done; goto done;
fcport->flags |= FCF_ASYNC_SENT;
fcport->logout_completed = 0;
sp->type = SRB_LOGIN_CMD; sp->type = SRB_LOGIN_CMD;
sp->name = "login"; sp->name = "login";
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
...@@ -165,8 +205,8 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -165,8 +205,8 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
} }
ql_dbg(ql_dbg_disc, vha, 0x2072, ql_dbg(ql_dbg_disc, vha, 0x2072,
"Async-login - hdl=%x, loopid=%x portid=%02x%02x%02x " "Async-login - %8phC hdl=%x, loopid=%x portid=%02x%02x%02x "
"retries=%d.\n", sp->handle, fcport->loop_id, "retries=%d.\n", fcport->port_name, sp->handle, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
fcport->login_retry); fcport->login_retry);
return rval; return rval;
...@@ -174,6 +214,7 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -174,6 +214,7 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
done_free_sp: done_free_sp:
sp->free(fcport->vha, sp); sp->free(fcport->vha, sp);
done: done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval; return rval;
} }
...@@ -184,6 +225,7 @@ qla2x00_async_logout_sp_done(void *data, void *ptr, int res) ...@@ -184,6 +225,7 @@ qla2x00_async_logout_sp_done(void *data, void *ptr, int res)
struct srb_iocb *lio = &sp->u.iocb_cmd; struct srb_iocb *lio = &sp->u.iocb_cmd;
struct scsi_qla_host *vha = (scsi_qla_host_t *)data; struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
sp->fcport->flags &= ~FCF_ASYNC_SENT;
if (!test_bit(UNLOADING, &vha->dpc_flags)) if (!test_bit(UNLOADING, &vha->dpc_flags))
qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport, qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport,
lio->u.logio.data); lio->u.logio.data);
...@@ -198,6 +240,7 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) ...@@ -198,6 +240,7 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
int rval; int rval;
rval = QLA_FUNCTION_FAILED; rval = QLA_FUNCTION_FAILED;
fcport->flags |= FCF_ASYNC_SENT;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp) if (!sp)
goto done; goto done;
...@@ -214,14 +257,16 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) ...@@ -214,14 +257,16 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
goto done_free_sp; goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0x2070, ql_dbg(ql_dbg_disc, vha, 0x2070,
"Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x.\n", "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x %8phC.\n",
sp->handle, fcport->loop_id, fcport->d_id.b.domain, sp->handle, fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa); fcport->d_id.b.area, fcport->d_id.b.al_pa,
fcport->port_name);
return rval; return rval;
done_free_sp: done_free_sp:
sp->free(fcport->vha, sp); sp->free(fcport->vha, sp);
done: done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval; return rval;
} }
...@@ -247,6 +292,7 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -247,6 +292,7 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
int rval; int rval;
rval = QLA_FUNCTION_FAILED; rval = QLA_FUNCTION_FAILED;
fcport->flags |= FCF_ASYNC_SENT;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp) if (!sp)
goto done; goto done;
...@@ -273,9 +319,817 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -273,9 +319,817 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
done_free_sp: done_free_sp:
sp->free(fcport->vha, sp); sp->free(fcport->vha, sp);
done: done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval;
}
static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha,
struct event_arg *ea)
{
fc_port_t *fcport, *conflict_fcport;
struct get_name_list_extended *e;
u16 i, n, found = 0, loop_id;
port_id_t id;
u64 wwn;
u8 opt = 0;
fcport = ea->fcport;
if (ea->rc) { /* rval */
if (fcport->login_retry == 0) {
fcport->login_retry = vha->hw->login_retry_count;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"GNL failed Port login retry %8phN, retry cnt=%d.\n",
fcport->port_name, fcport->login_retry);
}
return;
}
if (fcport->last_rscn_gen != fcport->rscn_gen) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC rscn gen changed rscn %d|%d \n",
__func__, fcport->port_name,
fcport->last_rscn_gen, fcport->rscn_gen);
qla24xx_post_gidpn_work(vha, fcport);
return;
} else if (fcport->last_login_gen != fcport->login_gen) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC login gen changed login %d|%d \n",
__func__, fcport->port_name,
fcport->last_login_gen, fcport->login_gen);
return;
}
n = ea->data[0] / sizeof(struct get_name_list_extended);
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC n %d %02x%02x%02x lid %d \n",
__func__, __LINE__, fcport->port_name, n,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, fcport->loop_id);
for (i = 0; i < n; i++) {
e = &vha->gnl.l[i];
wwn = wwn_to_u64(e->port_name);
if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE))
continue;
found = 1;
id.b.domain = e->port_id[2];
id.b.area = e->port_id[1];
id.b.al_pa = e->port_id[0];
id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(e->nport_handle);
loop_id = (loop_id & 0x7fff);
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s found %8phC CLS [%d|%d] ID[%02x%02x%02x|%02x%02x%02x] lid[%d|%d]\n",
__func__, fcport->port_name,
e->current_login_state, fcport->fw_login_state,
id.b.domain, id.b.area, id.b.al_pa,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, loop_id, fcport->loop_id);
if ((id.b24 != fcport->d_id.b24) ||
((fcport->loop_id != FC_NO_LOOP_ID) &&
(fcport->loop_id != loop_id))) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__, fcport->port_name);
qlt_schedule_sess_for_deletion(fcport, 1);
return;
}
fcport->loop_id = loop_id;
wwn = wwn_to_u64(fcport->port_name);
qlt_find_sess_invalidate_other(vha, wwn,
id, loop_id, &conflict_fcport);
if (conflict_fcport) {
/*
* Another share fcport share the same loop_id &
* nport id. Conflict fcport needs to finish
* cleanup before this fcport can proceed to login.
*/
conflict_fcport->conflict = fcport;
fcport->login_pause = 1;
}
switch (e->current_login_state) {
case DSC_LS_PRLI_COMP:
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gpdb\n",
__func__, __LINE__, fcport->port_name);
opt = PDO_FORCE_ADISC;
qla24xx_post_gpdb_work(vha, fcport, opt);
break;
case DSC_LS_PORT_UNAVAIL:
default:
if (fcport->loop_id == FC_NO_LOOP_ID) {
qla2x00_find_new_loop_id(vha, fcport);
fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
}
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC \n",
__func__, __LINE__, fcport->port_name);
qla24xx_fcport_handle_login(vha, fcport);
break;
}
}
if (!found) {
/* fw has no record of this port */
if (fcport->loop_id == FC_NO_LOOP_ID) {
qla2x00_find_new_loop_id(vha, fcport);
fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
} else {
for (i = 0; i < n; i++) {
e = &vha->gnl.l[i];
id.b.domain = e->port_id[0];
id.b.area = e->port_id[1];
id.b.al_pa = e->port_id[2];
id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(e->nport_handle);
if (fcport->d_id.b24 == id.b24) {
conflict_fcport =
qla2x00_find_fcport_by_wwpn(vha,
e->port_name, 0);
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__,
conflict_fcport->port_name);
qlt_schedule_sess_for_deletion
(conflict_fcport, 1);
}
if (fcport->loop_id == loop_id) {
/* FW already picked this loop id for another fcport */
qla2x00_find_new_loop_id(vha, fcport);
}
}
}
qla24xx_fcport_handle_login(vha, fcport);
}
} /* gnl_event */
static void
qla24xx_async_gnl_sp_done(void *v, void *s, int res)
{
struct scsi_qla_host *vha = (struct scsi_qla_host *)v;
struct srb *sp = (struct srb *)s;
unsigned long flags;
struct fc_port *fcport = NULL, *tf;
u16 i, n = 0, loop_id;
struct event_arg ea;
struct get_name_list_extended *e;
u64 wwn;
struct list_head h;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async done-%s res %x mb[1]=%x mb[2]=%x \n",
sp->name, res, sp->u.iocb_cmd.u.mbx.in_mb[1],
sp->u.iocb_cmd.u.mbx.in_mb[2]);
memset(&ea, 0, sizeof(ea));
ea.sp = sp;
ea.rc = res;
ea.event = FCME_GNL_DONE;
if (sp->u.iocb_cmd.u.mbx.in_mb[1] >=
sizeof(struct get_name_list_extended)) {
n = sp->u.iocb_cmd.u.mbx.in_mb[1] /
sizeof(struct get_name_list_extended);
ea.data[0] = sp->u.iocb_cmd.u.mbx.in_mb[1]; /* amnt xfered */
}
for (i = 0; i < n; i++) {
e = &vha->gnl.l[i];
loop_id = le16_to_cpu(e->nport_handle);
/* mask out reserve bit */
loop_id = (loop_id & 0x7fff);
set_bit(loop_id, vha->hw->loop_id_map);
wwn = wwn_to_u64(e->port_name);
ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xffff,
"%s %8phC %02x:%02x:%02x state %d/%d lid %x \n",
__func__, (void *)&wwn, e->port_id[2], e->port_id[1],
e->port_id[0], e->current_login_state, e->last_login_state,
(loop_id & 0x7fff));
}
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
vha->gnl.sent = 0;
INIT_LIST_HEAD(&h);
fcport = tf = NULL;
if (!list_empty(&vha->gnl.fcports))
list_splice_init(&vha->gnl.fcports, &h);
list_for_each_entry_safe(fcport, tf, &h, gnl_entry) {
list_del_init(&fcport->gnl_entry);
fcport->flags &= ~FCF_ASYNC_SENT;
ea.fcport = fcport;
qla2x00_fcport_event_handler(vha, &ea);
}
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
sp->free(vha, sp);
}
int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport)
{
srb_t *sp;
struct srb_iocb *mbx;
int rval = QLA_FUNCTION_FAILED;
unsigned long flags;
u16 *mb;
if (!vha->flags.online)
goto done;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-gnlist WWPN %8phC \n", fcport->port_name);
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
fcport->flags |= FCF_ASYNC_SENT;
fcport->disc_state = DSC_GNL;
fcport->last_rscn_gen = fcport->rscn_gen;
fcport->last_login_gen = fcport->login_gen;
list_add_tail(&fcport->gnl_entry, &vha->gnl.fcports);
if (vha->gnl.sent) {
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
rval = QLA_SUCCESS;
goto done;
}
vha->gnl.sent = 1;
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
sp->type = SRB_MB_IOCB;
sp->name = "gnlist";
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2);
mb = sp->u.iocb_cmd.u.mbx.out_mb;
mb[0] = MBC_PORT_NODE_NAME_LIST;
mb[1] = BIT_2 | BIT_3;
mb[2] = MSW(vha->gnl.ldma);
mb[3] = LSW(vha->gnl.ldma);
mb[6] = MSW(MSD(vha->gnl.ldma));
mb[7] = LSW(MSD(vha->gnl.ldma));
mb[8] = vha->gnl.size;
mb[9] = vha->vp_idx;
mbx = &sp->u.iocb_cmd;
mbx->timeout = qla2x00_async_iocb_timeout;
sp->done = qla24xx_async_gnl_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s - OUT WWPN %8phC hndl %x\n",
sp->name, fcport->port_name, sp->handle);
return rval;
done_free_sp:
sp->free(fcport->vha, sp);
done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval;
}
int qla24xx_post_gnl_work(struct scsi_qla_host *vha, fc_port_t *fcport)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_GNL);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.fcport.fcport = fcport;
return qla2x00_post_work(vha, e);
}
static
void qla24xx_async_gpdb_sp_done(void *v, void *s, int res)
{
struct scsi_qla_host *vha = (struct scsi_qla_host *)v;
struct srb *sp = (struct srb *)s;
struct qla_hw_data *ha = vha->hw;
uint64_t zero = 0;
struct port_database_24xx *pd;
fc_port_t *fcport = sp->fcport;
u16 *mb = sp->u.iocb_cmd.u.mbx.in_mb;
int rval = QLA_SUCCESS;
struct event_arg ea;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async done-%s res %x, WWPN %8phC mb[1]=%x mb[2]=%x \n",
sp->name, res, fcport->port_name, mb[1], mb[2]);
fcport->flags &= ~FCF_ASYNC_SENT;
if (res) {
rval = res;
goto gpd_error_out;
}
pd = (struct port_database_24xx *)sp->u.iocb_cmd.u.mbx.in;
/* Check for logged in state. */
if (pd->current_login_state != PDS_PRLI_COMPLETE &&
pd->last_login_state != PDS_PRLI_COMPLETE) {
ql_dbg(ql_dbg_mbx, vha, 0xffff,
"Unable to verify login-state (%x/%x) for "
"loop_id %x.\n", pd->current_login_state,
pd->last_login_state, fcport->loop_id);
rval = QLA_FUNCTION_FAILED;
goto gpd_error_out;
}
if (fcport->loop_id == FC_NO_LOOP_ID ||
(memcmp(fcport->port_name, (uint8_t *)&zero, 8) &&
memcmp(fcport->port_name, pd->port_name, 8))) {
/* We lost the device mid way. */
rval = QLA_NOT_LOGGED_IN;
goto gpd_error_out;
}
/* Names are little-endian. */
memcpy(fcport->node_name, pd->node_name, WWN_SIZE);
/* Get port_id of device. */
fcport->d_id.b.domain = pd->port_id[0];
fcport->d_id.b.area = pd->port_id[1];
fcport->d_id.b.al_pa = pd->port_id[2];
fcport->d_id.b.rsvd_1 = 0;
/* If not target must be initiator or unknown type. */
if ((pd->prli_svc_param_word_3[0] & BIT_4) == 0)
fcport->port_type = FCT_INITIATOR;
else
fcport->port_type = FCT_TARGET;
/* Passback COS information. */
fcport->supported_classes = (pd->flags & PDF_CLASS_2) ?
FC_COS_CLASS2 : FC_COS_CLASS3;
if (pd->prli_svc_param_word_3[0] & BIT_7) {
fcport->flags |= FCF_CONF_COMP_SUPPORTED;
fcport->conf_compl_supported = 1;
}
gpd_error_out:
memset(&ea, 0, sizeof(ea));
ea.event = FCME_GPDB_DONE;
ea.rc = rval;
ea.fcport = fcport;
ea.sp = sp;
qla2x00_fcport_event_handler(vha, &ea);
dma_pool_free(ha->s_dma_pool, sp->u.iocb_cmd.u.mbx.in,
sp->u.iocb_cmd.u.mbx.in_dma);
sp->free(vha, sp);
}
static int qla24xx_post_gpdb_work(struct scsi_qla_host *vha, fc_port_t *fcport,
u8 opt)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_GPDB);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.fcport.fcport = fcport;
e->u.fcport.opt = opt;
return qla2x00_post_work(vha, e);
}
int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt)
{
srb_t *sp;
struct srb_iocb *mbx;
int rval = QLA_FUNCTION_FAILED;
u16 *mb;
dma_addr_t pd_dma;
struct port_database_24xx *pd;
struct qla_hw_data *ha = vha->hw;
if (!vha->flags.online)
goto done;
fcport->flags |= FCF_ASYNC_SENT;
fcport->disc_state = DSC_GPDB;
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
if (pd == NULL) {
ql_log(ql_log_warn, vha, 0xffff,
"Failed to allocate port database structure.\n");
goto done_free_sp;
}
memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE));
sp->type = SRB_MB_IOCB;
sp->name = "gpdb";
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
mb = sp->u.iocb_cmd.u.mbx.out_mb;
mb[0] = MBC_GET_PORT_DATABASE;
mb[1] = fcport->loop_id;
mb[2] = MSW(pd_dma);
mb[3] = LSW(pd_dma);
mb[6] = MSW(MSD(pd_dma));
mb[7] = LSW(MSD(pd_dma));
mb[9] = vha->vp_idx;
mb[10] = opt;
mbx = &sp->u.iocb_cmd;
mbx->timeout = qla2x00_async_iocb_timeout;
mbx->u.mbx.in = (void *)pd;
mbx->u.mbx.in_dma = pd_dma;
sp->done = qla24xx_async_gpdb_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s %8phC hndl %x opt %x\n",
sp->name, fcport->port_name, sp->handle, opt);
return rval;
done_free_sp:
if (pd)
dma_pool_free(ha->s_dma_pool, pd, pd_dma);
sp->free(vha, sp);
done:
fcport->flags &= ~FCF_ASYNC_SENT;
qla24xx_post_gpdb_work(vha, fcport, opt);
return rval; return rval;
} }
static
void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea)
{
int rval = ea->rc;
fc_port_t *fcport = ea->fcport;
unsigned long flags;
fcport->flags &= ~FCF_ASYNC_SENT;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC DS %d LS %d rval %d\n", __func__, fcport->port_name,
fcport->disc_state, fcport->fw_login_state, rval);
if (ea->sp->gen2 != fcport->login_gen) {
/* target side must have changed it. */
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC generation changed rscn %d|%d login %d|%d \n",
__func__, fcport->port_name, fcport->last_rscn_gen,
fcport->rscn_gen, fcport->last_login_gen,
fcport->login_gen);
return;
} else if (ea->sp->gen1 != fcport->rscn_gen) {
ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post gidpn\n",
__func__, __LINE__, fcport->port_name);
qla24xx_post_gidpn_work(vha, fcport);
return;
}
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post del sess\n",
__func__, __LINE__, fcport->port_name);
qlt_schedule_sess_for_deletion_lock(fcport);
return;
}
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
ea->fcport->login_gen++;
ea->fcport->deleted = 0;
ea->fcport->logout_on_delete = 1;
if (!ea->fcport->login_succ && !IS_SW_RESV_ADDR(ea->fcport->d_id)) {
vha->fcport_count++;
ea->fcport->login_succ = 1;
if (!IS_IIDMA_CAPABLE(vha->hw) ||
!vha->hw->flags.gpsc_supported) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post upd_fcport fcp_cnt %d\n",
__func__, __LINE__, fcport->port_name,
vha->fcport_count);
qla24xx_post_upd_fcport_work(vha, fcport);
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gpsc fcp_cnt %d\n",
__func__, __LINE__, fcport->port_name,
vha->fcport_count);
qla24xx_post_gpsc_work(vha, fcport);
}
}
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
} /* gpdb event */
int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport)
{
if (fcport->login_retry == 0)
return 0;
if (fcport->scan_state != QLA_FCPORT_FOUND)
return 0;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d|%d retry %d lid %d\n",
__func__, fcport->port_name, fcport->disc_state,
fcport->fw_login_state, fcport->login_pause, fcport->flags,
fcport->conflict, fcport->last_rscn_gen, fcport->rscn_gen,
fcport->last_login_gen, fcport->login_gen, fcport->login_retry,
fcport->loop_id);
fcport->login_retry--;
if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) ||
(fcport->fw_login_state == DSC_LS_PLOGI_COMP) ||
(fcport->fw_login_state == DSC_LS_PRLI_PEND))
return 0;
/* for pure Target Mode. Login will not be initiated */
if (vha->host->active_mode == MODE_TARGET)
return 0;
if (fcport->flags & FCF_ASYNC_SENT) {
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
return 0;
}
switch (fcport->disc_state) {
case DSC_DELETED:
if (fcport->loop_id == FC_NO_LOOP_ID) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gnl\n",
__func__, __LINE__, fcport->port_name);
qla24xx_async_gnl(vha, fcport);
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post login\n",
__func__, __LINE__, fcport->port_name);
fcport->disc_state = DSC_LOGIN_PEND;
qla2x00_post_async_login_work(vha, fcport, NULL);
}
break;
case DSC_GNL:
if (fcport->login_pause) {
fcport->last_rscn_gen = fcport->rscn_gen;
fcport->last_login_gen = fcport->login_gen;
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
break;
}
if (fcport->flags & FCF_FCP2_DEVICE) {
u8 opt = PDO_FORCE_ADISC;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gpdb\n",
__func__, __LINE__, fcport->port_name);
fcport->disc_state = DSC_GPDB;
qla24xx_post_gpdb_work(vha, fcport, opt);
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post login \n",
__func__, __LINE__, fcport->port_name);
fcport->disc_state = DSC_LOGIN_PEND;
qla2x00_post_async_login_work(vha, fcport, NULL);
}
break;
case DSC_LOGIN_FAILED:
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gidpn \n",
__func__, __LINE__, fcport->port_name);
qla24xx_post_gidpn_work(vha, fcport);
break;
case DSC_LOGIN_COMPLETE:
/* recheck login state */
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gpdb \n",
__func__, __LINE__, fcport->port_name);
qla24xx_post_gpdb_work(vha, fcport, PDO_FORCE_ADISC);
break;
default:
break;
}
return 0;
}
static
void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea)
{
fcport->rscn_gen++;
ql_dbg(ql_dbg_disc, fcport->vha, 0xffff,
"%s %8phC DS %d LS %d\n",
__func__, fcport->port_name, fcport->disc_state,
fcport->fw_login_state);
if (fcport->flags & FCF_ASYNC_SENT)
return;
switch (fcport->disc_state) {
case DSC_DELETED:
case DSC_LOGIN_COMPLETE:
qla24xx_post_gidpn_work(fcport->vha, fcport);
break;
default:
break;
}
}
int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id,
u8 *port_name, void *pla)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_NEW_SESS);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.new_sess.id = *id;
e->u.new_sess.pla = pla;
memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE);
return qla2x00_post_work(vha, e);
}
static
int qla24xx_handle_delete_done_event(scsi_qla_host_t *vha,
struct event_arg *ea)
{
fc_port_t *fcport = ea->fcport;
if (test_bit(UNLOADING, &vha->dpc_flags))
return 0;
switch (vha->host->active_mode) {
case MODE_INITIATOR:
case MODE_DUAL:
if (fcport->scan_state == QLA_FCPORT_FOUND)
qla24xx_fcport_handle_login(vha, fcport);
break;
case MODE_TARGET:
default:
/* no-op */
break;
}
return 0;
}
static
void qla24xx_handle_relogin_event(scsi_qla_host_t *vha,
struct event_arg *ea)
{
fc_port_t *fcport = ea->fcport;
if (fcport->scan_state != QLA_FCPORT_FOUND) {
fcport->login_retry++;
return;
}
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phC DS %d LS %d P %d del %d cnfl %p rscn %d|%d login %d|%d fl %x\n",
__func__, fcport->port_name, fcport->disc_state,
fcport->fw_login_state, fcport->login_pause,
fcport->deleted, fcport->conflict,
fcport->last_rscn_gen, fcport->rscn_gen,
fcport->last_login_gen, fcport->login_gen,
fcport->flags);
if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) ||
(fcport->fw_login_state == DSC_LS_PLOGI_COMP) ||
(fcport->fw_login_state == DSC_LS_PRLI_PEND))
return;
if (fcport->flags & FCF_ASYNC_SENT) {
fcport->login_retry++;
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
return;
}
if (fcport->disc_state == DSC_DELETE_PEND) {
fcport->login_retry++;
return;
}
if (fcport->last_rscn_gen != fcport->rscn_gen) {
ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post gidpn\n",
__func__, __LINE__, fcport->port_name);
qla24xx_async_gidpn(vha, fcport);
return;
}
qla24xx_fcport_handle_login(vha, fcport);
}
void qla2x00_fcport_event_handler(scsi_qla_host_t *vha,
struct event_arg *ea)
{
fc_port_t *fcport;
int rc;
switch (ea->event) {
case FCME_RELOGIN:
if (test_bit(UNLOADING, &vha->dpc_flags))
return;
qla24xx_handle_relogin_event(vha, ea);
break;
case FCME_RSCN:
if (test_bit(UNLOADING, &vha->dpc_flags))
return;
fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1);
if (!fcport) {
/* cable moved */
rc = qla24xx_post_gpnid_work(vha, &ea->id);
if (rc) {
ql_log(ql_log_warn, vha, 0xffff,
"RSCN GPNID work failed %02x%02x%02x\n",
ea->id.b.domain, ea->id.b.area,
ea->id.b.al_pa);
}
} else {
ea->fcport = fcport;
qla24xx_handle_rscn_event(fcport, ea);
}
break;
case FCME_GIDPN_DONE:
qla24xx_handle_gidpn_event(vha, ea);
break;
case FCME_GNL_DONE:
qla24xx_handle_gnl_done_event(vha, ea);
break;
case FCME_GPSC_DONE:
qla24xx_post_upd_fcport_work(vha, ea->fcport);
break;
case FCME_PLOGI_DONE: /* Initiator side sent LLIOCB */
qla24xx_handle_plogi_done_event(vha, ea);
break;
case FCME_GPDB_DONE:
qla24xx_handle_gpdb_event(vha, ea);
break;
case FCME_GPNID_DONE:
qla24xx_handle_gpnid_event(vha, ea);
break;
case FCME_DELETE_DONE:
qla24xx_handle_delete_done_event(vha, ea);
break;
default:
BUG_ON(1);
break;
}
}
static void static void
qla2x00_tmf_iocb_timeout(void *data) qla2x00_tmf_iocb_timeout(void *data)
{ {
...@@ -441,59 +1295,65 @@ qla24xx_async_abort_command(srb_t *sp) ...@@ -441,59 +1295,65 @@ qla24xx_async_abort_command(srb_t *sp)
return qla24xx_async_abort_cmd(sp); return qla24xx_async_abort_cmd(sp);
} }
void static void
qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, qla24xx_handle_plogi_done_event(struct scsi_qla_host *vha, struct event_arg *ea)
uint16_t *data)
{ {
int rval; port_id_t cid; /* conflict Nport id */
switch (data[0]) { switch (ea->data[0]) {
case MBS_COMMAND_COMPLETE: case MBS_COMMAND_COMPLETE:
/* /*
* Driver must validate login state - If PRLI not complete, * Driver must validate login state - If PRLI not complete,
* force a relogin attempt via implicit LOGO, PLOGI, and PRLI * force a relogin attempt via implicit LOGO, PLOGI, and PRLI
* requests. * requests.
*/ */
rval = qla2x00_get_port_database(vha, fcport, 0); ql_dbg(ql_dbg_disc, vha, 0xffff,
if (rval == QLA_NOT_LOGGED_IN) { "%s %d %8phC post gpdb\n",
fcport->flags &= ~FCF_ASYNC_SENT; __func__, __LINE__, ea->fcport->port_name);
fcport->flags |= FCF_LOGIN_NEEDED; ea->fcport->chip_reset = vha->hw->chip_reset;
set_bit(RELOGIN_NEEDED, &vha->dpc_flags); ea->fcport->logout_on_delete = 1;
break; qla24xx_post_gpdb_work(vha, ea->fcport, 0);
}
if (rval != QLA_SUCCESS) {
qla2x00_post_async_logout_work(vha, fcport, NULL);
qla2x00_post_async_login_work(vha, fcport, NULL);
break;
}
if (fcport->flags & FCF_FCP2_DEVICE) {
qla2x00_post_async_adisc_work(vha, fcport, data);
break;
}
qla2x00_update_fcport(vha, fcport);
break; break;
case MBS_COMMAND_ERROR: case MBS_COMMAND_ERROR:
fcport->flags &= ~FCF_ASYNC_SENT; ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC cmd error %x\n",
if (data[1] & QLA_LOGIO_LOGIN_RETRIED) __func__, __LINE__, ea->fcport->port_name, ea->data[1]);
ea->fcport->flags &= ~FCF_ASYNC_SENT;
ea->fcport->disc_state = DSC_LOGIN_FAILED;
if (ea->data[1] & QLA_LOGIO_LOGIN_RETRIED)
set_bit(RELOGIN_NEEDED, &vha->dpc_flags); set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
else else
qla2x00_mark_device_lost(vha, fcport, 1, 0); qla2x00_mark_device_lost(vha, ea->fcport, 1, 0);
break;
case MBS_PORT_ID_USED:
fcport->loop_id = data[1];
qla2x00_post_async_logout_work(vha, fcport, NULL);
qla2x00_post_async_login_work(vha, fcport, NULL);
break; break;
case MBS_LOOP_ID_USED: case MBS_LOOP_ID_USED:
fcport->loop_id++; /* data[1] = IO PARAM 1 = nport ID */
rval = qla2x00_find_new_loop_id(vha, fcport); cid.b.domain = (ea->iop[1] >> 16) & 0xff;
if (rval != QLA_SUCCESS) { cid.b.area = (ea->iop[1] >> 8) & 0xff;
fcport->flags &= ~FCF_ASYNC_SENT; cid.b.al_pa = ea->iop[1] & 0xff;
qla2x00_mark_device_lost(vha, fcport, 1, 0); cid.b.rsvd_1 = 0;
break;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC LoopID 0x%x in use post gnl\n",
__func__, __LINE__, ea->fcport->port_name,
ea->fcport->loop_id);
if (IS_SW_RESV_ADDR(cid)) {
set_bit(ea->fcport->loop_id, vha->hw->loop_id_map);
ea->fcport->loop_id = FC_NO_LOOP_ID;
} else {
qla2x00_clear_loop_id(ea->fcport);
} }
qla2x00_post_async_login_work(vha, fcport, NULL); qla24xx_post_gnl_work(vha, ea->fcport);
break;
case MBS_PORT_ID_USED:
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC NPortId %02x%02x%02x inuse post gidpn\n",
__func__, __LINE__, ea->fcport->port_name,
ea->fcport->d_id.b.domain, ea->fcport->d_id.b.area,
ea->fcport->d_id.b.al_pa);
qla2x00_clear_loop_id(ea->fcport);
qla24xx_post_gidpn_work(vha, ea->fcport);
break; break;
} }
return; return;
...@@ -503,10 +1363,9 @@ void ...@@ -503,10 +1363,9 @@ void
qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport,
uint16_t *data) uint16_t *data)
{ {
/* Don't re-login in target mode */ qla2x00_mark_device_lost(vha, fcport, 1, 0);
if (!fcport->tgt_session)
qla2x00_mark_device_lost(vha, fcport, 1, 0);
qlt_logo_completion_handler(fcport, data[0]); qlt_logo_completion_handler(fcport, data[0]);
fcport->login_gen++;
return; return;
} }
...@@ -709,7 +1568,8 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) ...@@ -709,7 +1568,8 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
} }
} }
if (qla_ini_mode_enabled(vha)) if (qla_ini_mode_enabled(vha) ||
qla_dual_mode_enabled(vha))
rval = qla2x00_init_rings(vha); rval = qla2x00_init_rings(vha);
ha->flags.chip_reset_done = 1; ha->flags.chip_reset_done = 1;
...@@ -2968,8 +3828,14 @@ qla2x00_rport_del(void *data) ...@@ -2968,8 +3828,14 @@ qla2x00_rport_del(void *data)
rport = fcport->drport ? fcport->drport: fcport->rport; rport = fcport->drport ? fcport->drport: fcport->rport;
fcport->drport = NULL; fcport->drport = NULL;
spin_unlock_irqrestore(fcport->vha->host->host_lock, flags); spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
if (rport) if (rport) {
ql_dbg(ql_dbg_disc, fcport->vha, 0xffff,
"%s %8phN. rport %p roles %x \n",
__func__, fcport->port_name, rport,
rport->roles);
fc_remote_port_delete(rport); fc_remote_port_delete(rport);
}
} }
/** /**
...@@ -2995,9 +3861,42 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) ...@@ -2995,9 +3861,42 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED); qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
fcport->supported_classes = FC_COS_UNSPECIFIED; fcport->supported_classes = FC_COS_UNSPECIFIED;
fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma,
GFP_ATOMIC);
fcport->disc_state = DSC_DELETED;
fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
fcport->deleted = QLA_SESS_DELETED;
fcport->login_retry = vha->hw->login_retry_count;
fcport->login_retry = 5;
fcport->logout_on_delete = 1;
if (!fcport->ct_desc.ct_sns) {
ql_log(ql_log_warn, vha, 0xffff,
"Failed to allocate ct_sns request.\n");
kfree(fcport);
fcport = NULL;
}
INIT_WORK(&fcport->del_work, qla24xx_delete_sess_fn);
INIT_LIST_HEAD(&fcport->gnl_entry);
INIT_LIST_HEAD(&fcport->list);
return fcport; return fcport;
} }
void
qla2x00_free_fcport(fc_port_t *fcport)
{
if (fcport->ct_desc.ct_sns) {
dma_free_coherent(&fcport->vha->hw->pdev->dev,
sizeof(struct ct_sns_pkt), fcport->ct_desc.ct_sns,
fcport->ct_desc.ct_sns_dma);
fcport->ct_desc.ct_sns = NULL;
}
kfree(fcport);
}
/* /*
* qla2x00_configure_loop * qla2x00_configure_loop
* Updates Fibre Channel Device Database with what is actually on loop. * Updates Fibre Channel Device Database with what is actually on loop.
...@@ -3334,6 +4233,7 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -3334,6 +4233,7 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
} }
} }
/* qla2x00_reg_remote_port is reserved for Initiator Mode only.*/
static void static void
qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
{ {
...@@ -3364,6 +4264,12 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -3364,6 +4264,12 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcport->port_type == FCT_TARGET) if (fcport->port_type == FCT_TARGET)
rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET; rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %8phN. rport %p is %s mode \n",
__func__, fcport->port_name, rport,
(fcport->port_type == FCT_TARGET) ? "tgt" : "ini");
fc_remote_port_rolechg(rport, rport_ids.roles); fc_remote_port_rolechg(rport, rport_ids.roles);
} }
...@@ -3387,20 +4293,45 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -3387,20 +4293,45 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{ {
fcport->vha = vha; fcport->vha = vha;
if (IS_SW_RESV_ADDR(fcport->d_id))
return;
ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %8phC \n",
__func__, fcport->port_name);
if (IS_QLAFX00(vha->hw)) { if (IS_QLAFX00(vha->hw)) {
qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_set_fcport_state(fcport, FCS_ONLINE);
goto reg_port; goto reg_port;
} }
fcport->login_retry = 0; fcport->login_retry = 0;
fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
fcport->disc_state = DSC_LOGIN_COMPLETE;
fcport->deleted = 0;
fcport->logout_on_delete = 1;
qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_set_fcport_state(fcport, FCS_ONLINE);
qla2x00_iidma_fcport(vha, fcport); qla2x00_iidma_fcport(vha, fcport);
qla24xx_update_fcport_fcp_prio(vha, fcport); qla24xx_update_fcport_fcp_prio(vha, fcport);
reg_port: reg_port:
if (qla_ini_mode_enabled(vha)) switch (vha->host->active_mode) {
case MODE_INITIATOR:
qla2x00_reg_remote_port(vha, fcport);
break;
case MODE_TARGET:
if (!vha->vha_tgt.qla_tgt->tgt_stop &&
!vha->vha_tgt.qla_tgt->tgt_stopped)
qlt_fc_port_added(vha, fcport);
break;
case MODE_DUAL:
qla2x00_reg_remote_port(vha, fcport); qla2x00_reg_remote_port(vha, fcport);
if (!vha->vha_tgt.qla_tgt->tgt_stop &&
!vha->vha_tgt.qla_tgt->tgt_stopped)
qlt_fc_port_added(vha, fcport);
break;
default:
break;
}
} }
/* /*
...@@ -3418,13 +4349,11 @@ static int ...@@ -3418,13 +4349,11 @@ static int
qla2x00_configure_fabric(scsi_qla_host_t *vha) qla2x00_configure_fabric(scsi_qla_host_t *vha)
{ {
int rval; int rval;
fc_port_t *fcport, *fcptemp; fc_port_t *fcport;
uint16_t next_loopid;
uint16_t mb[MAILBOX_REGISTER_COUNT]; uint16_t mb[MAILBOX_REGISTER_COUNT];
uint16_t loop_id; uint16_t loop_id;
LIST_HEAD(new_fcports); LIST_HEAD(new_fcports);
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
int discovery_gen; int discovery_gen;
/* If FL port exists, then SNS is present */ /* If FL port exists, then SNS is present */
...@@ -3443,6 +4372,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3443,6 +4372,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
vha->device_flags |= SWITCH_FOUND; vha->device_flags |= SWITCH_FOUND;
do { do {
qla2x00_mgmt_svr_login(vha);
/* FDMI support. */ /* FDMI support. */
if (ql2xfdmienable && if (ql2xfdmienable &&
test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags)) test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags))
...@@ -3489,9 +4420,6 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3489,9 +4420,6 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
} }
} }
#define QLA_FCPORT_SCAN 1
#define QLA_FCPORT_FOUND 2
list_for_each_entry(fcport, &vha->vp_fcports, list) { list_for_each_entry(fcport, &vha->vp_fcports, list) {
fcport->scan_state = QLA_FCPORT_SCAN; fcport->scan_state = QLA_FCPORT_SCAN;
} }
...@@ -3504,174 +4432,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3504,174 +4432,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
* will be newer than discovery_gen. */ * will be newer than discovery_gen. */
qlt_do_generation_tick(vha, &discovery_gen); qlt_do_generation_tick(vha, &discovery_gen);
rval = qla2x00_find_all_fabric_devs(vha, &new_fcports); rval = qla2x00_find_all_fabric_devs(vha);
if (rval != QLA_SUCCESS) if (rval != QLA_SUCCESS)
break; break;
/*
* Logout all previous fabric devices marked lost, except
* FCP2 devices.
*/
list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
continue;
if (fcport->scan_state == QLA_FCPORT_SCAN) {
if (qla_ini_mode_enabled(base_vha) &&
atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_FCP2_DEVICE) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
ha->isp_ops->fabric_logout(vha,
fcport->loop_id,
fcport->d_id.b.domain,
fcport->d_id.b.area,
fcport->d_id.b.al_pa);
qla2x00_clear_loop_id(fcport);
}
} else if (!qla_ini_mode_enabled(base_vha)) {
/*
* In target mode, explicitly kill
* sessions and log out of devices
* that are gone, so that we don't
* end up with an initiator using the
* wrong ACL (if the fabric recycles
* an FC address and we have a stale
* session around) and so that we don't
* report initiators that are no longer
* on the fabric.
*/
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf077,
"port gone, logging out/killing session: "
"%8phC state 0x%x flags 0x%x fc4_type 0x%x "
"scan_state %d\n",
fcport->port_name,
atomic_read(&fcport->state),
fcport->flags, fcport->fc4_type,
fcport->scan_state);
qlt_fc_port_deleted(vha, fcport,
discovery_gen);
}
}
}
/* Starting free loop ID. */
next_loopid = ha->min_external_loopid;
/*
* Scan through our port list and login entries that need to be
* logged in.
*/
list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (atomic_read(&vha->loop_down_timer) ||
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
(fcport->flags & FCF_LOGIN_NEEDED) == 0)
continue;
/*
* If we're not an initiator, skip looking for devices
* and logging in. There's no reason for us to do it,
* and it seems to actively cause problems in target
* mode if we race with the initiator logging into us
* (we might get the "port ID used" status back from
* our login command and log out the initiator, which
* seems to cause havoc).
*/
if (!qla_ini_mode_enabled(base_vha)) {
if (fcport->scan_state == QLA_FCPORT_FOUND) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf078,
"port %8phC state 0x%x flags 0x%x fc4_type 0x%x "
"scan_state %d (initiator mode disabled; skipping "
"login)\n", fcport->port_name,
atomic_read(&fcport->state),
fcport->flags, fcport->fc4_type,
fcport->scan_state);
}
continue;
}
if (fcport->loop_id == FC_NO_LOOP_ID) {
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(
base_vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
}
/* Login and update database */
qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
}
/* Exit if out of loop IDs. */
if (rval != QLA_SUCCESS) {
break;
}
/*
* Login and add the new devices to our port list.
*/
list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
if (atomic_read(&vha->loop_down_timer) ||
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
/*
* If we're not an initiator, skip looking for devices
* and logging in. There's no reason for us to do it,
* and it seems to actively cause problems in target
* mode if we race with the initiator logging into us
* (we might get the "port ID used" status back from
* our login command and log out the initiator, which
* seems to cause havoc).
*/
if (qla_ini_mode_enabled(base_vha)) {
/* Find a new loop ID to use. */
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(base_vha,
fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
/* Login and update database */
qla2x00_fabric_dev_login(vha, fcport,
&next_loopid);
} else {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf079,
"new port %8phC state 0x%x flags 0x%x fc4_type "
"0x%x scan_state %d (initiator mode disabled; "
"skipping login)\n",
fcport->port_name,
atomic_read(&fcport->state),
fcport->flags, fcport->fc4_type,
fcport->scan_state);
}
list_move_tail(&fcport->list, &vha->vp_fcports);
}
} while (0); } while (0);
/* Free all new device structures not processed. */ if (rval)
list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
list_del(&fcport->list);
kfree(fcport);
}
if (rval) {
ql_dbg(ql_dbg_disc, vha, 0x2068, ql_dbg(ql_dbg_disc, vha, 0x2068,
"Configure fabric error exit rval=%d.\n", rval); "Configure fabric error exit rval=%d.\n", rval);
}
return (rval); return (rval);
} }
...@@ -3690,12 +4458,11 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3690,12 +4458,11 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
* Kernel context. * Kernel context.
*/ */
static int static int
qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha)
struct list_head *new_fcports)
{ {
int rval; int rval;
uint16_t loop_id; uint16_t loop_id;
fc_port_t *fcport, *new_fcport, *fcptemp; fc_port_t *fcport, *new_fcport;
int found; int found;
sw_info_t *swl; sw_info_t *swl;
...@@ -3704,6 +4471,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3704,6 +4471,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
port_id_t wrap = {}, nxt_d_id; port_id_t wrap = {}, nxt_d_id;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
unsigned long flags;
rval = QLA_SUCCESS; rval = QLA_SUCCESS;
...@@ -3724,9 +4492,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3724,9 +4492,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
swl = NULL; swl = NULL;
} else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) { } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) {
swl = NULL; swl = NULL;
} else if (ql2xiidmaenable && } else if (qla2x00_gfpn_id(vha, swl) != QLA_SUCCESS) {
qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) { swl = NULL;
qla2x00_gpsc(vha, swl);
} }
/* If other queries succeeded probe for FC-4 type */ /* If other queries succeeded probe for FC-4 type */
...@@ -3788,11 +4555,6 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3788,11 +4555,6 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
ql_log(ql_log_warn, vha, 0x2064, ql_log(ql_log_warn, vha, 0x2064,
"SNS scan failed -- assuming " "SNS scan failed -- assuming "
"zero-entry result.\n"); "zero-entry result.\n");
list_for_each_entry_safe(fcport, fcptemp,
new_fcports, list) {
list_del(&fcport->list);
kfree(fcport);
}
rval = QLA_SUCCESS; rval = QLA_SUCCESS;
break; break;
} }
...@@ -3835,6 +4597,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3835,6 +4597,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
new_fcport->fc4_type != FC4_TYPE_UNKNOWN)) new_fcport->fc4_type != FC4_TYPE_UNKNOWN))
continue; continue;
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
/* Locate matching device in database. */ /* Locate matching device in database. */
found = 0; found = 0;
list_for_each_entry(fcport, &vha->vp_fcports, list) { list_for_each_entry(fcport, &vha->vp_fcports, list) {
...@@ -3857,7 +4621,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3857,7 +4621,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
*/ */
if (fcport->d_id.b24 == new_fcport->d_id.b24 && if (fcport->d_id.b24 == new_fcport->d_id.b24 &&
(atomic_read(&fcport->state) == FCS_ONLINE || (atomic_read(&fcport->state) == FCS_ONLINE ||
!qla_ini_mode_enabled(base_vha))) { (vha->host->active_mode == MODE_TARGET))) {
break; break;
} }
...@@ -3877,7 +4641,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3877,7 +4641,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
* Log it out if still logged in and mark it for * Log it out if still logged in and mark it for
* relogin later. * relogin later.
*/ */
if (!qla_ini_mode_enabled(base_vha)) { if (qla_tgt_mode_enabled(base_vha)) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf080, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf080,
"port changed FC ID, %8phC" "port changed FC ID, %8phC"
" old %x:%x:%x (loop_id 0x%04x)-> new %x:%x:%x\n", " old %x:%x:%x (loop_id 0x%04x)-> new %x:%x:%x\n",
...@@ -3895,25 +4659,19 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3895,25 +4659,19 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
fcport->d_id.b24 = new_fcport->d_id.b24; fcport->d_id.b24 = new_fcport->d_id.b24;
fcport->flags |= FCF_LOGIN_NEEDED; fcport->flags |= FCF_LOGIN_NEEDED;
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_FCP2_DEVICE) == 0 &&
(fcport->flags & FCF_ASYNC_SENT) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
qla2x00_clear_loop_id(fcport);
}
break; break;
} }
if (found) if (found) {
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
continue; continue;
}
/* If device was not in our fcports list, then add it. */ /* If device was not in our fcports list, then add it. */
new_fcport->scan_state = QLA_FCPORT_FOUND; new_fcport->scan_state = QLA_FCPORT_FOUND;
list_add_tail(&new_fcport->list, new_fcports); list_add_tail(&new_fcport->list, &vha->vp_fcports);
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
/* Allocate a new replacement fcport. */ /* Allocate a new replacement fcport. */
nxt_d_id.b24 = new_fcport->d_id.b24; nxt_d_id.b24 = new_fcport->d_id.b24;
...@@ -3927,8 +4685,44 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3927,8 +4685,44 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
new_fcport->d_id.b24 = nxt_d_id.b24; new_fcport->d_id.b24 = nxt_d_id.b24;
} }
kfree(new_fcport); qla2x00_free_fcport(new_fcport);
/*
* Logout all previous fabric dev marked lost, except FCP2 devices.
*/
list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
(fcport->flags & FCF_LOGIN_NEEDED) == 0)
continue;
if (fcport->scan_state == QLA_FCPORT_SCAN) {
if ((qla_dual_mode_enabled(vha) ||
qla_ini_mode_enabled(vha)) &&
atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_FCP2_DEVICE) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__,
fcport->port_name);
qlt_schedule_sess_for_deletion_lock
(fcport);
continue;
}
}
}
if (fcport->scan_state == QLA_FCPORT_FOUND)
qla24xx_fcport_handle_login(vha, fcport);
}
return (rval); return (rval);
} }
...@@ -3980,64 +4774,6 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev) ...@@ -3980,64 +4774,6 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
return (rval); return (rval);
} }
/*
* qla2x00_fabric_dev_login
* Login fabric target device and update FC port database.
*
* Input:
* ha: adapter state pointer.
* fcport: port structure list pointer.
* next_loopid: contains value of a new loop ID that can be used
* by the next login attempt.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
static int
qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *next_loopid)
{
int rval;
uint8_t opts;
struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
if (IS_ALOGIO_CAPABLE(ha)) {
if (fcport->flags & FCF_ASYNC_SENT)
return rval;
fcport->flags |= FCF_ASYNC_SENT;
rval = qla2x00_post_async_login_work(vha, fcport, NULL);
if (!rval)
return rval;
}
fcport->flags &= ~FCF_ASYNC_SENT;
rval = qla2x00_fabric_login(vha, fcport, next_loopid);
if (rval == QLA_SUCCESS) {
/* Send an ADISC to FCP2 devices.*/
opts = 0;
if (fcport->flags & FCF_FCP2_DEVICE)
opts |= BIT_1;
rval = qla2x00_get_port_database(vha, fcport, opts);
if (rval != QLA_SUCCESS) {
ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
qla2x00_mark_device_lost(vha, fcport, 1, 0);
} else {
qla2x00_update_fcport(vha, fcport);
}
} else {
/* Retry Login. */
qla2x00_mark_device_lost(vha, fcport, 1, 0);
}
return (rval);
}
/* /*
* qla2x00_fabric_login * qla2x00_fabric_login
...@@ -4329,13 +5065,6 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) ...@@ -4329,13 +5065,6 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha)
spin_unlock_irqrestore(&ha->vport_slock, flags); spin_unlock_irqrestore(&ha->vport_slock, flags);
qla2x00_rport_del(fcport); qla2x00_rport_del(fcport);
/*
* Release the target mode FC NEXUS in
* qla_target.c, if target mod is enabled.
*/
qlt_fc_port_deleted(vha, fcport,
base_vha->total_fcport_update_gen);
spin_lock_irqsave(&ha->vport_slock, flags); spin_lock_irqsave(&ha->vport_slock, flags);
} }
} }
...@@ -4718,6 +5447,8 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) ...@@ -4718,6 +5447,8 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
if (!(IS_P3P_TYPE(ha))) if (!(IS_P3P_TYPE(ha)))
ha->isp_ops->reset_chip(vha); ha->isp_ops->reset_chip(vha);
ha->chip_reset++;
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
if (atomic_read(&vha->loop_state) != LOOP_DOWN) { if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
atomic_set(&vha->loop_state, LOOP_DOWN); atomic_set(&vha->loop_state, LOOP_DOWN);
...@@ -4772,8 +5503,6 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) ...@@ -4772,8 +5503,6 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
/* Requeue all commands in outstanding command list. */ /* Requeue all commands in outstanding command list. */
qla2x00_abort_all_cmds(vha, DID_RESET << 16); qla2x00_abort_all_cmds(vha, DID_RESET << 16);
} }
ha->chip_reset++;
/* memory barrier */ /* memory barrier */
wmb(); wmb();
} }
...@@ -4969,7 +5698,6 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) ...@@ -4969,7 +5698,6 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
if (!status) { if (!status) {
/* Issue a marker after FW becomes ready. */ /* Issue a marker after FW becomes ready. */
qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
} }
...@@ -5197,7 +5925,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) ...@@ -5197,7 +5925,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
rval = 1; rval = 1;
} }
if (!qla_ini_mode_enabled(vha)) { if (qla_tgt_mode_enabled(vha)) {
/* Don't enable full login after initial LIP */ /* Don't enable full login after initial LIP */
nv->firmware_options_1 &= cpu_to_le32(~BIT_13); nv->firmware_options_1 &= cpu_to_le32(~BIT_13);
/* Don't enable LIP full login for initiator */ /* Don't enable LIP full login for initiator */
......
...@@ -166,8 +166,8 @@ qla2x00_set_fcport_state(fc_port_t *fcport, int state) ...@@ -166,8 +166,8 @@ qla2x00_set_fcport_state(fc_port_t *fcport, int state)
/* Don't print state transitions during initial allocation of fcport */ /* Don't print state transitions during initial allocation of fcport */
if (old_state && old_state != state) { if (old_state && old_state != state) {
ql_dbg(ql_dbg_disc, fcport->vha, 0x207d, ql_dbg(ql_dbg_disc, fcport->vha, 0x207d,
"FCPort state transitioned from %s to %s - " "FCPort %8phC state transitioned from %s to %s - "
"portid=%02x%02x%02x.\n", "portid=%02x%02x%02x.\n", fcport->port_name,
port_state_str[old_state], port_state_str[state], port_state_str[old_state], port_state_str[state],
fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa); fcport->d_id.b.al_pa);
...@@ -263,6 +263,7 @@ qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag) ...@@ -263,6 +263,7 @@ qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag)
memset(sp, 0, sizeof(*sp)); memset(sp, 0, sizeof(*sp));
sp->fcport = fcport; sp->fcport = fcport;
sp->iocbs = 1; sp->iocbs = 1;
sp->vha = vha;
done: done:
if (!sp) if (!sp)
QLA_VHA_MARK_NOT_BUSY(vha); QLA_VHA_MARK_NOT_BUSY(vha);
...@@ -285,7 +286,7 @@ qla2x00_init_timer(srb_t *sp, unsigned long tmo) ...@@ -285,7 +286,7 @@ qla2x00_init_timer(srb_t *sp, unsigned long tmo)
sp->u.iocb_cmd.timer.function = qla2x00_sp_timeout; sp->u.iocb_cmd.timer.function = qla2x00_sp_timeout;
add_timer(&sp->u.iocb_cmd.timer); add_timer(&sp->u.iocb_cmd.timer);
sp->free = qla2x00_sp_free; sp->free = qla2x00_sp_free;
if ((IS_QLAFX00(sp->fcport->vha->hw)) && if ((IS_QLAFX00(((scsi_qla_host_t *)sp->vha)->hw)) &&
(sp->type == SRB_FXIOCB_DCMD)) (sp->type == SRB_FXIOCB_DCMD))
init_completion(&sp->u.iocb_cmd.u.fxiocb.fxiocb_comp); init_completion(&sp->u.iocb_cmd.u.fxiocb.fxiocb_comp);
if (sp->type == SRB_ELS_DCMD) if (sp->type == SRB_ELS_DCMD)
......
...@@ -2247,7 +2247,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) ...@@ -2247,7 +2247,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
logio->control_flags = logio->control_flags =
cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
if (!sp->fcport->tgt_session || if (!sp->fcport->se_sess ||
!sp->fcport->keep_nport_handle) !sp->fcport->keep_nport_handle)
logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT); logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT);
logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
...@@ -3079,19 +3079,69 @@ qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) ...@@ -3079,19 +3079,69 @@ qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb)
wmb(); wmb();
} }
static void
qla2x00_mb_iocb(srb_t *sp, struct mbx_24xx_entry *mbx)
{
int i, sz;
mbx->entry_type = MBX_IOCB_TYPE;
mbx->handle = sp->handle;
sz = min(ARRAY_SIZE(mbx->mb), ARRAY_SIZE(sp->u.iocb_cmd.u.mbx.out_mb));
for (i = 0; i < sz; i++)
mbx->mb[i] = cpu_to_le16(sp->u.iocb_cmd.u.mbx.out_mb[i]);
}
static void
qla2x00_ctpthru_cmd_iocb(srb_t *sp, struct ct_entry_24xx *ct_pkt)
{
sp->u.iocb_cmd.u.ctarg.iocb = ct_pkt;
qla24xx_prep_ms_iocb(sp->vha, &sp->u.iocb_cmd.u.ctarg);
ct_pkt->handle = sp->handle;
}
static void qla2x00_send_notify_ack_iocb(srb_t *sp,
struct nack_to_isp *nack)
{
struct imm_ntfy_from_isp *ntfy = sp->u.iocb_cmd.u.nack.ntfy;
nack->entry_type = NOTIFY_ACK_TYPE;
nack->entry_count = 1;
nack->ox_id = ntfy->ox_id;
nack->u.isp24.handle = sp->handle;
nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
nack->u.isp24.flags = ntfy->u.isp24.flags &
cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB);
}
nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
nack->u.isp24.status = ntfy->u.isp24.status;
nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle;
nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
nack->u.isp24.srr_flags = 0;
nack->u.isp24.srr_reject_code = 0;
nack->u.isp24.srr_reject_code_expl = 0;
nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
}
int int
qla2x00_start_sp(srb_t *sp) qla2x00_start_sp(srb_t *sp)
{ {
int rval; int rval;
struct qla_hw_data *ha = sp->fcport->vha->hw; scsi_qla_host_t *vha = (scsi_qla_host_t *)sp->vha;
struct qla_hw_data *ha = vha->hw;
void *pkt; void *pkt;
unsigned long flags; unsigned long flags;
rval = QLA_FUNCTION_FAILED; rval = QLA_FUNCTION_FAILED;
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
pkt = qla2x00_alloc_iocbs(sp->fcport->vha, sp); pkt = qla2x00_alloc_iocbs(vha, sp);
if (!pkt) { if (!pkt) {
ql_log(ql_log_warn, sp->fcport->vha, 0x700c, ql_log(ql_log_warn, vha, 0x700c,
"qla2x00_alloc_iocbs failed.\n"); "qla2x00_alloc_iocbs failed.\n");
goto done; goto done;
} }
...@@ -3139,12 +3189,23 @@ qla2x00_start_sp(srb_t *sp) ...@@ -3139,12 +3189,23 @@ qla2x00_start_sp(srb_t *sp)
case SRB_ELS_DCMD: case SRB_ELS_DCMD:
qla24xx_els_logo_iocb(sp, pkt); qla24xx_els_logo_iocb(sp, pkt);
break; break;
case SRB_CT_PTHRU_CMD:
qla2x00_ctpthru_cmd_iocb(sp, pkt);
break;
case SRB_MB_IOCB:
qla2x00_mb_iocb(sp, pkt);
break;
case SRB_NACK_PLOGI:
case SRB_NACK_PRLI:
case SRB_NACK_LOGO:
qla2x00_send_notify_ack_iocb(sp, pkt);
break;
default: default:
break; break;
} }
wmb(); wmb();
qla2x00_start_iocbs(sp->fcport->vha, ha->req_q_map[0]); qla2x00_start_iocbs(vha, ha->req_q_map[0]);
done: done:
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
return rval; return rval;
......
...@@ -561,14 +561,50 @@ qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry) ...@@ -561,14 +561,50 @@ qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry)
return ret; return ret;
} }
static inline fc_port_t * fc_port_t *
qla2x00_find_fcport_by_loopid(scsi_qla_host_t *vha, uint16_t loop_id) qla2x00_find_fcport_by_loopid(scsi_qla_host_t *vha, uint16_t loop_id)
{ {
fc_port_t *fcport; fc_port_t *f, *tf;
f = tf = NULL;
list_for_each_entry_safe(f, tf, &vha->vp_fcports, list)
if (f->loop_id == loop_id)
return f;
return NULL;
}
fc_port_t *
qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *vha, u8 *wwpn, u8 incl_deleted)
{
fc_port_t *f, *tf;
f = tf = NULL;
list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
if (memcmp(f->port_name, wwpn, WWN_SIZE) == 0) {
if (incl_deleted)
return f;
else if (f->deleted == 0)
return f;
}
}
return NULL;
}
list_for_each_entry(fcport, &vha->vp_fcports, list) fc_port_t *
if (fcport->loop_id == loop_id) qla2x00_find_fcport_by_nportid(scsi_qla_host_t *vha, port_id_t *id,
return fcport; u8 incl_deleted)
{
fc_port_t *f, *tf;
f = tf = NULL;
list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
if (f->d_id.b24 == id->b24) {
if (incl_deleted)
return f;
else if (f->deleted == 0)
return f;
}
}
return NULL; return NULL;
} }
...@@ -934,7 +970,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) ...@@ -934,7 +970,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
ql_dbg(ql_dbg_async, vha, 0x508a, ql_dbg(ql_dbg_async, vha, 0x508a,
"Marking port lost loopid=%04x portid=%06x.\n", "Marking port lost loopid=%04x portid=%06x.\n",
fcport->loop_id, fcport->d_id.b24); fcport->loop_id, fcport->d_id.b24);
qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); if (qla_ini_mode_enabled(vha)) {
qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
fcport->logout_on_delete = 0;
qlt_schedule_sess_for_deletion_lock(fcport);
}
break; break;
global_port_update: global_port_update:
...@@ -1024,27 +1064,17 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) ...@@ -1024,27 +1064,17 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
if (qla2x00_is_a_vp_did(vha, rscn_entry)) if (qla2x00_is_a_vp_did(vha, rscn_entry))
break; break;
/*
* Search for the rport related to this RSCN entry and mark it
* as lost.
*/
list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (atomic_read(&fcport->state) != FCS_ONLINE)
continue;
if (fcport->d_id.b24 == rscn_entry) {
qla2x00_mark_device_lost(vha, fcport, 0, 0);
break;
}
}
atomic_set(&vha->loop_down_timer, 0); atomic_set(&vha->loop_down_timer, 0);
vha->flags.management_server_logged_in = 0; vha->flags.management_server_logged_in = 0;
{
struct event_arg ea;
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); memset(&ea, 0, sizeof(ea));
set_bit(RSCN_UPDATE, &vha->dpc_flags); ea.event = FCME_RSCN;
qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry); ea.id.b24 = rscn_entry;
qla2x00_fcport_event_handler(vha, &ea);
}
break; break;
/* case MBA_RIO_RESPONSE: */ /* case MBA_RIO_RESPONSE: */
case MBA_ZIO_RESPONSE: case MBA_ZIO_RESPONSE:
ql_dbg(ql_dbg_async, vha, 0x5015, ql_dbg(ql_dbg_async, vha, 0x5015,
...@@ -1235,7 +1265,8 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, ...@@ -1235,7 +1265,8 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
index = LSW(pkt->handle); index = LSW(pkt->handle);
if (index >= req->num_outstanding_cmds) { if (index >= req->num_outstanding_cmds) {
ql_log(ql_log_warn, vha, 0x5031, ql_log(ql_log_warn, vha, 0x5031,
"Invalid command index (%x).\n", index); "Invalid command index (%x) type %8ph.\n",
index, iocb);
if (IS_P3P_TYPE(ha)) if (IS_P3P_TYPE(ha))
set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
else else
...@@ -1346,6 +1377,49 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, ...@@ -1346,6 +1377,49 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
sp->done(vha, sp, 0); sp->done(vha, sp, 0);
} }
static void
qla24xx_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
struct mbx_24xx_entry *pkt)
{
const char func[] = "MBX-IOCB2";
srb_t *sp;
struct srb_iocb *si;
u16 sz, i;
int res;
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp)
return;
si = &sp->u.iocb_cmd;
sz = min(ARRAY_SIZE(pkt->mb), ARRAY_SIZE(sp->u.iocb_cmd.u.mbx.in_mb));
for (i = 0; i < sz; i++)
si->u.mbx.in_mb[i] = le16_to_cpu(pkt->mb[i]);
res = (si->u.mbx.in_mb[0] & MBS_MASK);
sp->done(vha, sp, res);
}
static void
qla24xxx_nack_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
struct nack_to_isp *pkt)
{
const char func[] = "nack";
srb_t *sp;
int res = 0;
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp)
return;
if (pkt->u.isp2x.status != cpu_to_le16(NOTIFY_ACK_SUCCESS))
res = QLA_FUNCTION_FAILED;
sp->done(vha, sp, res);
}
static void static void
qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
sts_entry_t *pkt, int iocb_type) sts_entry_t *pkt, int iocb_type)
...@@ -1356,50 +1430,63 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, ...@@ -1356,50 +1430,63 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
struct bsg_job *bsg_job; struct bsg_job *bsg_job;
struct fc_bsg_reply *bsg_reply; struct fc_bsg_reply *bsg_reply;
uint16_t comp_status; uint16_t comp_status;
int res; int res = 0;
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp) if (!sp)
return; return;
bsg_job = sp->u.bsg_job; switch (sp->type) {
bsg_reply = bsg_job->reply; case SRB_CT_CMD:
bsg_job = sp->u.bsg_job;
type = "ct pass-through"; bsg_reply = bsg_job->reply;
comp_status = le16_to_cpu(pkt->comp_status); type = "ct pass-through";
/* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT comp_status = le16_to_cpu(pkt->comp_status);
* fc payload to the caller
*/ /*
bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; * return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
bsg_job->reply_len = sizeof(struct fc_bsg_reply); * fc payload to the caller
*/
if (comp_status != CS_COMPLETE) { bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
if (comp_status == CS_DATA_UNDERRUN) { bsg_job->reply_len = sizeof(struct fc_bsg_reply);
res = DID_OK << 16;
bsg_reply->reply_payload_rcv_len = if (comp_status != CS_COMPLETE) {
le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len); if (comp_status == CS_DATA_UNDERRUN) {
res = DID_OK << 16;
ql_log(ql_log_warn, vha, 0x5048, bsg_reply->reply_payload_rcv_len =
"CT pass-through-%s error " le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len);
"comp_status-status=0x%x total_byte = 0x%x.\n",
type, comp_status, ql_log(ql_log_warn, vha, 0x5048,
bsg_reply->reply_payload_rcv_len); "CT pass-through-%s error comp_status=0x%x total_byte=0x%x.\n",
} else { type, comp_status,
ql_log(ql_log_warn, vha, 0x5049, bsg_reply->reply_payload_rcv_len);
"CT pass-through-%s error " } else {
"comp_status-status=0x%x.\n", type, comp_status); ql_log(ql_log_warn, vha, 0x5049,
res = DID_ERROR << 16; "CT pass-through-%s error comp_status=0x%x.\n",
bsg_reply->reply_payload_rcv_len = 0; type, comp_status);
} res = DID_ERROR << 16;
ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035, bsg_reply->reply_payload_rcv_len = 0;
(uint8_t *)pkt, sizeof(*pkt)); }
} else { ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035,
res = DID_OK << 16; (uint8_t *)pkt, sizeof(*pkt));
bsg_reply->reply_payload_rcv_len = } else {
bsg_job->reply_payload.payload_len; res = DID_OK << 16;
bsg_job->reply_len = 0; bsg_reply->reply_payload_rcv_len =
bsg_job->reply_payload.payload_len;
bsg_job->reply_len = 0;
}
break;
case SRB_CT_PTHRU_CMD:
/*
* borrowing sts_entry_24xx.comp_status.
* same location as ct_entry_24xx.comp_status
*/
res = qla2x00_chk_ms_status(vha, (ms_iocb_entry_t *)pkt,
(struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp,
sp->name);
break;
} }
sp->done(vha, sp, res); sp->done(vha, sp, res);
...@@ -1440,6 +1527,15 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, ...@@ -1440,6 +1527,15 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
"Completing %s: (%p) type=%d.\n", type, sp, sp->type); "Completing %s: (%p) type=%d.\n", type, sp, sp->type);
sp->done(vha, sp, 0); sp->done(vha, sp, 0);
return; return;
case SRB_CT_PTHRU_CMD:
/* borrowing sts_entry_24xx.comp_status.
same location as ct_entry_24xx.comp_status
*/
res = qla2x00_chk_ms_status(vha, (ms_iocb_entry_t *)pkt,
(struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp,
sp->name);
sp->done(vha, sp, res);
return;
default: default:
ql_dbg(ql_dbg_user, vha, 0x503e, ql_dbg(ql_dbg_user, vha, 0x503e,
"Unrecognized SRB: (%p) type=%d.\n", sp, sp->type); "Unrecognized SRB: (%p) type=%d.\n", sp, sp->type);
...@@ -1566,6 +1662,8 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, ...@@ -1566,6 +1662,8 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
iop[0] = le32_to_cpu(logio->io_parameter[0]); iop[0] = le32_to_cpu(logio->io_parameter[0]);
iop[1] = le32_to_cpu(logio->io_parameter[1]); iop[1] = le32_to_cpu(logio->io_parameter[1]);
lio->u.logio.iop[0] = iop[0];
lio->u.logio.iop[1] = iop[1];
switch (iop[0]) { switch (iop[0]) {
case LSC_SCODE_PORTID_USED: case LSC_SCODE_PORTID_USED:
data[0] = MBS_PORT_ID_USED; data[0] = MBS_PORT_ID_USED;
...@@ -2074,6 +2172,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ...@@ -2074,6 +2172,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
int res = 0; int res = 0;
uint16_t state_flags = 0; uint16_t state_flags = 0;
uint16_t retry_delay = 0; uint16_t retry_delay = 0;
uint8_t no_logout = 0;
sts = (sts_entry_t *) pkt; sts = (sts_entry_t *) pkt;
sts24 = (struct sts_entry_24xx *) pkt; sts24 = (struct sts_entry_24xx *) pkt;
...@@ -2334,6 +2433,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ...@@ -2334,6 +2433,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break; break;
case CS_PORT_LOGGED_OUT: case CS_PORT_LOGGED_OUT:
no_logout = 1;
case CS_PORT_CONFIG_CHG: case CS_PORT_CONFIG_CHG:
case CS_PORT_BUSY: case CS_PORT_BUSY:
case CS_INCOMPLETE: case CS_INCOMPLETE:
...@@ -2356,14 +2456,21 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ...@@ -2356,14 +2456,21 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break; break;
} }
ql_dbg(ql_dbg_io, fcport->vha, 0x3021, if (atomic_read(&fcport->state) == FCS_ONLINE) {
"Port to be marked lost on fcport=%02x%02x%02x, current " ql_dbg(ql_dbg_disc, fcport->vha, 0x3021,
"port state= %s.\n", fcport->d_id.b.domain, "Port to be marked lost on fcport=%02x%02x%02x, current "
fcport->d_id.b.area, fcport->d_id.b.al_pa, "port state= %s comp_status %x.\n", fcport->d_id.b.domain,
port_state_str[atomic_read(&fcport->state)]); fcport->d_id.b.area, fcport->d_id.b.al_pa,
port_state_str[atomic_read(&fcport->state)],
comp_status);
if (no_logout)
fcport->logout_on_delete = 0;
if (atomic_read(&fcport->state) == FCS_ONLINE)
qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
qlt_schedule_sess_for_deletion_lock(fcport);
}
break; break;
case CS_ABORTED: case CS_ABORTED:
...@@ -2627,10 +2734,16 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, ...@@ -2627,10 +2734,16 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
} }
case ABTS_RESP_24XX: case ABTS_RESP_24XX:
case CTIO_TYPE7: case CTIO_TYPE7:
case NOTIFY_ACK_TYPE:
case CTIO_CRC2: case CTIO_CRC2:
qlt_response_pkt_all_vps(vha, (response_t *)pkt); qlt_response_pkt_all_vps(vha, (response_t *)pkt);
break; break;
case NOTIFY_ACK_TYPE:
if (pkt->handle == QLA_TGT_SKIP_HANDLE)
qlt_response_pkt_all_vps(vha, (response_t *)pkt);
else
qla24xxx_nack_iocb_entry(vha, rsp->req,
(struct nack_to_isp *)pkt);
break;
case MARKER_TYPE: case MARKER_TYPE:
/* Do nothing in this case, this check is to prevent it /* Do nothing in this case, this check is to prevent it
* from falling into default case * from falling into default case
...@@ -2640,6 +2753,10 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, ...@@ -2640,6 +2753,10 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla24xx_abort_iocb_entry(vha, rsp->req, qla24xx_abort_iocb_entry(vha, rsp->req,
(struct abort_entry_24xx *)pkt); (struct abort_entry_24xx *)pkt);
break; break;
case MBX_IOCB_TYPE:
qla24xx_mbx_iocb_entry(vha, rsp->req,
(struct mbx_24xx_entry *)pkt);
break;
default: default:
/* Type Not Supported. */ /* Type Not Supported. */
ql_dbg(ql_dbg_async, vha, 0x5042, ql_dbg(ql_dbg_async, vha, 0x5042,
...@@ -2656,8 +2773,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, ...@@ -2656,8 +2773,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
if (IS_P3P_TYPE(ha)) { if (IS_P3P_TYPE(ha)) {
struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; struct device_reg_82xx __iomem *reg = &ha->iobase->isp82;
WRT_REG_DWORD(&reg->rsp_q_out[0], rsp->ring_index); WRT_REG_DWORD(&reg->rsp_q_out[0], rsp->ring_index);
} else } else {
WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index);
}
} }
static void static void
......
...@@ -1637,94 +1637,6 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) ...@@ -1637,94 +1637,6 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
return rval; return rval;
} }
/*
* qla2x00_get_node_name_list
* Issue get node name list mailbox command, kmalloc()
* and return the resulting list. Caller must kfree() it!
*
* Input:
* ha = adapter state pointer.
* out_data = resulting list
* out_len = length of the resulting list
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len)
{
struct qla_hw_data *ha = vha->hw;
struct qla_port_24xx_data *list = NULL;
void *pmap;
mbx_cmd_t mc;
dma_addr_t pmap_dma;
ulong dma_size;
int rval, left;
left = 1;
while (left > 0) {
dma_size = left * sizeof(*list);
pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size,
&pmap_dma, GFP_KERNEL);
if (!pmap) {
ql_log(ql_log_warn, vha, 0x113f,
"%s(%ld): DMA Alloc failed of %ld\n",
__func__, vha->host_no, dma_size);
rval = QLA_MEMORY_ALLOC_FAILED;
goto out;
}
mc.mb[0] = MBC_PORT_NODE_NAME_LIST;
mc.mb[1] = BIT_1 | BIT_3;
mc.mb[2] = MSW(pmap_dma);
mc.mb[3] = LSW(pmap_dma);
mc.mb[6] = MSW(MSD(pmap_dma));
mc.mb[7] = LSW(MSD(pmap_dma));
mc.mb[8] = dma_size;
mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8;
mc.in_mb = MBX_0|MBX_1;
mc.tov = 30;
mc.flags = MBX_DMA_IN;
rval = qla2x00_mailbox_command(vha, &mc);
if (rval != QLA_SUCCESS) {
if ((mc.mb[0] == MBS_COMMAND_ERROR) &&
(mc.mb[1] == 0xA)) {
left += le16_to_cpu(mc.mb[2]) /
sizeof(struct qla_port_24xx_data);
goto restart;
}
goto out_free;
}
left = 0;
list = kmemdup(pmap, dma_size, GFP_KERNEL);
if (!list) {
ql_log(ql_log_warn, vha, 0x1140,
"%s(%ld): failed to allocate node names list "
"structure.\n", __func__, vha->host_no);
rval = QLA_MEMORY_ALLOC_FAILED;
goto out_free;
}
restart:
dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
}
*out_data = list;
*out_len = dma_size;
out:
return rval;
out_free:
dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
return rval;
}
/* /*
* qla2x00_get_port_database * qla2x00_get_port_database
......
...@@ -1043,6 +1043,34 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) ...@@ -1043,6 +1043,34 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha)
return (return_status); return (return_status);
} }
static inline int test_fcport_count(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
unsigned long flags;
int res;
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
ql_dbg(ql_dbg_init, vha, 0xffff,
"tgt %p, fcport_count=%d\n",
vha, vha->fcport_count);
res = (vha->fcport_count == 0);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return res;
}
/*
* qla2x00_wait_for_sess_deletion can only be called from remove_one.
* it has dependency on UNLOADING flag to stop device discovery
*/
static void
qla2x00_wait_for_sess_deletion(scsi_qla_host_t *vha)
{
qla2x00_mark_all_devices_lost(vha, 0);
wait_event(vha->fcport_waitQ, test_fcport_count(vha));
}
/* /*
* qla2x00_wait_for_hba_ready * qla2x00_wait_for_hba_ready
* Wait till the HBA is ready before doing driver unload * Wait till the HBA is ready before doing driver unload
...@@ -2904,6 +2932,18 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -2904,6 +2932,18 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret) if (ret)
goto probe_init_failed; goto probe_init_failed;
base_vha->gnl.size = (ha->max_loop_id + 1) *
sizeof(struct get_name_list_extended);
base_vha->gnl.l = dma_alloc_coherent(&ha->pdev->dev,
base_vha->gnl.size, &base_vha->gnl.ldma, GFP_KERNEL);
INIT_LIST_HEAD(&base_vha->gnl.fcports);
if (base_vha->gnl.l == NULL) {
ql_log(ql_log_fatal, base_vha, 0xffff,
"Alloc failed for name list.\n");
goto probe_init_failed;
}
/* Alloc arrays of request and response ring ptrs */ /* Alloc arrays of request and response ring ptrs */
if (!qla2x00_alloc_queues(ha, req, rsp)) { if (!qla2x00_alloc_queues(ha, req, rsp)) {
ql_log(ql_log_fatal, base_vha, 0x003d, ql_log(ql_log_fatal, base_vha, 0x003d,
...@@ -3123,7 +3163,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -3123,7 +3163,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ql_dbg(ql_dbg_init, base_vha, 0x00f2, ql_dbg(ql_dbg_init, base_vha, 0x00f2,
"Init done and hba is online.\n"); "Init done and hba is online.\n");
if (qla_ini_mode_enabled(base_vha)) if (qla_ini_mode_enabled(base_vha) ||
qla_dual_mode_enabled(base_vha))
scsi_scan_host(host); scsi_scan_host(host);
else else
ql_dbg(ql_dbg_init, base_vha, 0x0122, ql_dbg(ql_dbg_init, base_vha, 0x0122,
...@@ -3372,21 +3413,26 @@ qla2x00_remove_one(struct pci_dev *pdev) ...@@ -3372,21 +3413,26 @@ qla2x00_remove_one(struct pci_dev *pdev)
* resources. * resources.
*/ */
if (!atomic_read(&pdev->enable_cnt)) { if (!atomic_read(&pdev->enable_cnt)) {
dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size,
base_vha->gnl.l, base_vha->gnl.ldma);
scsi_host_put(base_vha->host); scsi_host_put(base_vha->host);
kfree(ha); kfree(ha);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
return; return;
} }
qla2x00_wait_for_hba_ready(base_vha); qla2x00_wait_for_hba_ready(base_vha);
/* if UNLOAD flag is already set, then continue unload, /*
* if UNLOAD flag is already set, then continue unload,
* where it was set first. * where it was set first.
*/ */
if (test_bit(UNLOADING, &base_vha->dpc_flags)) if (test_bit(UNLOADING, &base_vha->dpc_flags))
return; return;
set_bit(UNLOADING, &base_vha->dpc_flags); set_bit(UNLOADING, &base_vha->dpc_flags);
dma_free_coherent(&ha->pdev->dev,
base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma);
if (IS_QLAFX00(ha)) if (IS_QLAFX00(ha))
qlafx00_driver_shutdown(base_vha, 20); qlafx00_driver_shutdown(base_vha, 20);
...@@ -3535,10 +3581,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -3535,10 +3581,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
qla2xxx_wake_dpc(base_vha); qla2xxx_wake_dpc(base_vha);
} else { } else {
int now; int now;
if (rport) if (rport) {
ql_dbg(ql_dbg_disc, fcport->vha, 0xffff,
"%s %8phN. rport %p roles %x \n",
__func__, fcport->port_name, rport,
rport->roles);
fc_remote_port_delete(rport); fc_remote_port_delete(rport);
}
qlt_do_generation_tick(vha, &now); qlt_do_generation_tick(vha, &now);
qlt_fc_port_deleted(vha, fcport, now);
} }
} }
...@@ -3581,7 +3631,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, ...@@ -3581,7 +3631,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
fcport->login_retry = vha->hw->login_retry_count; fcport->login_retry = vha->hw->login_retry_count;
ql_dbg(ql_dbg_disc, vha, 0x2067, ql_dbg(ql_dbg_disc, vha, 0x2067,
"Port login retry %8phN, id = 0x%04x retry cnt=%d.\n", "Port login retry %8phN, lid 0x%04x retry cnt=%d.\n",
fcport->port_name, fcport->loop_id, fcport->login_retry); fcport->port_name, fcport->loop_id, fcport->login_retry);
} }
} }
...@@ -3604,7 +3654,13 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer) ...@@ -3604,7 +3654,13 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
{ {
fc_port_t *fcport; fc_port_t *fcport;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Mark all dev lost\n");
list_for_each_entry(fcport, &vha->vp_fcports, list) { list_for_each_entry(fcport, &vha->vp_fcports, list) {
fcport->scan_state = 0;
qlt_schedule_sess_for_deletion_lock(fcport);
if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx) if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx)
continue; continue;
...@@ -4219,6 +4275,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, ...@@ -4219,6 +4275,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
spin_lock_init(&vha->work_lock); spin_lock_init(&vha->work_lock);
spin_lock_init(&vha->cmd_list_lock); spin_lock_init(&vha->cmd_list_lock);
init_waitqueue_head(&vha->fcport_waitQ);
sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no); sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
ql_dbg(ql_dbg_init, vha, 0x0041, ql_dbg(ql_dbg_init, vha, 0x0041,
...@@ -4232,7 +4289,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, ...@@ -4232,7 +4289,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
return vha; return vha;
} }
static struct qla_work_evt * struct qla_work_evt *
qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type) qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
{ {
struct qla_work_evt *e; struct qla_work_evt *e;
...@@ -4254,7 +4311,7 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type) ...@@ -4254,7 +4311,7 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
return e; return e;
} }
static int int
qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e) qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
{ {
unsigned long flags; unsigned long flags;
...@@ -4315,7 +4372,6 @@ int qla2x00_post_async_##name##_work( \ ...@@ -4315,7 +4372,6 @@ int qla2x00_post_async_##name##_work( \
} }
qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN); qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN);
qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT); qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE); qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC); qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC);
...@@ -4368,6 +4424,67 @@ qlafx00_post_aenfx_work(struct scsi_qla_host *vha, uint32_t evtcode, ...@@ -4368,6 +4424,67 @@ qlafx00_post_aenfx_work(struct scsi_qla_host *vha, uint32_t evtcode,
return qla2x00_post_work(vha, e); return qla2x00_post_work(vha, e);
} }
int qla24xx_post_upd_fcport_work(struct scsi_qla_host *vha, fc_port_t *fcport)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_UPD_FCPORT);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.fcport.fcport = fcport;
return qla2x00_post_work(vha, e);
}
static
void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e)
{
unsigned long flags;
fc_port_t *fcport = NULL;
struct qlt_plogi_ack_t *pla =
(struct qlt_plogi_ack_t *)e->u.new_sess.pla;
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
fcport = qla2x00_find_fcport_by_wwpn(vha, e->u.new_sess.port_name, 1);
if (fcport) {
fcport->d_id = e->u.new_sess.id;
if (pla) {
fcport->fw_login_state = DSC_LS_PLOGI_PEND;
qlt_plogi_ack_link(vha, pla, fcport, QLT_PLOGI_LINK_SAME_WWN);
/* we took an extra ref_count to prevent PLOGI ACK when
* fcport/sess has not been created.
*/
pla->ref_count--;
}
} else {
fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (fcport) {
fcport->d_id = e->u.new_sess.id;
fcport->scan_state = QLA_FCPORT_FOUND;
fcport->flags |= FCF_FABRIC_DEVICE;
fcport->fw_login_state = DSC_LS_PLOGI_PEND;
memcpy(fcport->port_name, e->u.new_sess.port_name,
WWN_SIZE);
list_add_tail(&fcport->list, &vha->vp_fcports);
if (pla) {
qlt_plogi_ack_link(vha, pla, fcport,
QLT_PLOGI_LINK_SAME_WWN);
pla->ref_count--;
}
}
}
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
if (fcport) {
if (pla)
qlt_plogi_ack_unref(vha, pla);
else
qla24xx_async_gnl(vha, fcport);
}
}
void void
qla2x00_do_work(struct scsi_qla_host *vha) qla2x00_do_work(struct scsi_qla_host *vha)
{ {
...@@ -4394,10 +4511,6 @@ qla2x00_do_work(struct scsi_qla_host *vha) ...@@ -4394,10 +4511,6 @@ qla2x00_do_work(struct scsi_qla_host *vha)
qla2x00_async_login(vha, e->u.logio.fcport, qla2x00_async_login(vha, e->u.logio.fcport,
e->u.logio.data); e->u.logio.data);
break; break;
case QLA_EVT_ASYNC_LOGIN_DONE:
qla2x00_async_login_done(vha, e->u.logio.fcport,
e->u.logio.data);
break;
case QLA_EVT_ASYNC_LOGOUT: case QLA_EVT_ASYNC_LOGOUT:
qla2x00_async_logout(vha, e->u.logio.fcport); qla2x00_async_logout(vha, e->u.logio.fcport);
break; break;
...@@ -4419,6 +4532,34 @@ qla2x00_do_work(struct scsi_qla_host *vha) ...@@ -4419,6 +4532,34 @@ qla2x00_do_work(struct scsi_qla_host *vha)
case QLA_EVT_AENFX: case QLA_EVT_AENFX:
qlafx00_process_aen(vha, e); qlafx00_process_aen(vha, e);
break; break;
case QLA_EVT_GIDPN:
qla24xx_async_gidpn(vha, e->u.fcport.fcport);
break;
case QLA_EVT_GPNID:
qla24xx_async_gpnid(vha, &e->u.gpnid.id);
break;
case QLA_EVT_GPNID_DONE:
qla24xx_async_gpnid_done(vha, e->u.iosb.sp);
break;
case QLA_EVT_NEW_SESS:
qla24xx_create_new_sess(vha, e);
break;
case QLA_EVT_GPDB:
qla24xx_async_gpdb(vha, e->u.fcport.fcport,
e->u.fcport.opt);
break;
case QLA_EVT_GPSC:
qla24xx_async_gpsc(vha, e->u.fcport.fcport);
break;
case QLA_EVT_UPD_FCPORT:
qla2x00_update_fcport(vha, e->u.fcport.fcport);
break;
case QLA_EVT_GNL:
qla24xx_async_gnl(vha, e->u.fcport.fcport);
break;
case QLA_EVT_NACK:
qla24xx_do_nack_work(vha, e);
break;
} }
if (e->flags & QLA_EVT_FLAG_FREE) if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e); kfree(e);
...@@ -4435,9 +4576,7 @@ void qla2x00_relogin(struct scsi_qla_host *vha) ...@@ -4435,9 +4576,7 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
{ {
fc_port_t *fcport; fc_port_t *fcport;
int status; int status;
uint16_t next_loopid = 0; struct event_arg ea;
struct qla_hw_data *ha = vha->hw;
uint16_t data[2];
list_for_each_entry(fcport, &vha->vp_fcports, list) { list_for_each_entry(fcport, &vha->vp_fcports, list) {
/* /*
...@@ -4448,77 +4587,38 @@ void qla2x00_relogin(struct scsi_qla_host *vha) ...@@ -4448,77 +4587,38 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) { fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) {
fcport->login_retry--; fcport->login_retry--;
if (fcport->flags & FCF_FABRIC_DEVICE) { if (fcport->flags & FCF_FABRIC_DEVICE) {
if (fcport->flags & FCF_FCP2_DEVICE) ql_dbg(ql_dbg_disc, fcport->vha, 0xffff,
ha->isp_ops->fabric_logout(vha, "%s %8phC DS %d LS %d\n", __func__,
fcport->loop_id, fcport->port_name, fcport->disc_state,
fcport->d_id.b.domain, fcport->fw_login_state);
fcport->d_id.b.area, memset(&ea, 0, sizeof(ea));
fcport->d_id.b.al_pa); ea.event = FCME_RELOGIN;
ea.fcport = fcport;
if (fcport->loop_id == FC_NO_LOOP_ID) { qla2x00_fcport_event_handler(vha, &ea);
fcport->loop_id = next_loopid = } else {
ha->min_external_loopid;
status = qla2x00_find_new_loop_id(
vha, fcport);
if (status != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
}
if (IS_ALOGIO_CAPABLE(ha)) {
fcport->flags |= FCF_ASYNC_SENT;
data[0] = 0;
data[1] = QLA_LOGIO_LOGIN_RETRIED;
status = qla2x00_post_async_login_work(
vha, fcport, data);
if (status == QLA_SUCCESS)
continue;
/* Attempt a retry. */
status = 1;
} else {
status = qla2x00_fabric_login(vha,
fcport, &next_loopid);
if (status == QLA_SUCCESS) {
int status2;
uint8_t opts;
opts = 0;
if (fcport->flags &
FCF_FCP2_DEVICE)
opts |= BIT_1;
status2 =
qla2x00_get_port_database(
vha, fcport, opts);
if (status2 != QLA_SUCCESS)
status = 1;
}
}
} else
status = qla2x00_local_device_login(vha, status = qla2x00_local_device_login(vha,
fcport); fcport);
if (status == QLA_SUCCESS) {
fcport->old_loop_id = fcport->loop_id;
ql_dbg(ql_dbg_disc, vha, 0x2003,
"Port login OK: logged in ID 0x%x.\n",
fcport->loop_id);
qla2x00_update_fcport(vha, fcport);
} else if (status == 1) {
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
/* retry the login again */
ql_dbg(ql_dbg_disc, vha, 0x2007,
"Retrying %d login again loop_id 0x%x.\n",
fcport->login_retry,
fcport->loop_id);
} else {
fcport->login_retry = 0;
}
if (status == QLA_SUCCESS) { if (fcport->login_retry == 0 &&
fcport->old_loop_id = fcport->loop_id; status != QLA_SUCCESS)
qla2x00_clear_loop_id(fcport);
ql_dbg(ql_dbg_disc, vha, 0x2003,
"Port login OK: logged in ID 0x%x.\n",
fcport->loop_id);
qla2x00_update_fcport(vha, fcport);
} else if (status == 1) {
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
/* retry the login again */
ql_dbg(ql_dbg_disc, vha, 0x2007,
"Retrying %d login again loop_id 0x%x.\n",
fcport->login_retry, fcport->loop_id);
} else {
fcport->login_retry = 0;
} }
if (fcport->login_retry == 0 && status != QLA_SUCCESS)
qla2x00_clear_loop_id(fcport);
} }
if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break; break;
...@@ -5182,7 +5282,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) ...@@ -5182,7 +5282,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
struct pci_dev *pdev = ha->pdev; struct pci_dev *pdev = ha->pdev;
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
/* if UNLOAD flag is already set, then continue unload, /*
* if UNLOAD flag is already set, then continue unload,
* where it was set first. * where it was set first.
*/ */
if (test_bit(UNLOADING, &base_vha->dpc_flags)) if (test_bit(UNLOADING, &base_vha->dpc_flags))
...@@ -5191,6 +5292,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) ...@@ -5191,6 +5292,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
ql_log(ql_log_warn, base_vha, 0x015b, ql_log(ql_log_warn, base_vha, 0x015b,
"Disabling adapter.\n"); "Disabling adapter.\n");
qla2x00_wait_for_sess_deletion(base_vha);
set_bit(UNLOADING, &base_vha->dpc_flags); set_bit(UNLOADING, &base_vha->dpc_flags);
qla2x00_delete_all_vps(ha, base_vha); qla2x00_delete_all_vps(ha, base_vha);
......
...@@ -118,6 +118,9 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, ...@@ -118,6 +118,9 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha,
uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan); uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan);
static void qlt_send_term_imm_notif(struct scsi_qla_host *vha, static void qlt_send_term_imm_notif(struct scsi_qla_host *vha,
struct imm_ntfy_from_isp *imm, int ha_locked); struct imm_ntfy_from_isp *imm, int ha_locked);
static struct fc_port *qlt_create_sess(struct scsi_qla_host *vha,
fc_port_t *fcport, bool local);
void qlt_unreg_sess(struct fc_port *sess);
/* /*
* Global Variables * Global Variables
*/ */
...@@ -378,6 +381,247 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt) ...@@ -378,6 +381,247 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt)
/* /*
* All qlt_plogi_ack_t operations are protected by hardware_lock * All qlt_plogi_ack_t operations are protected by hardware_lock
*/ */
static int qla24xx_post_nack_work(struct scsi_qla_host *vha, fc_port_t *fcport,
struct imm_ntfy_from_isp *ntfy, int type)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_NACK);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.nack.fcport = fcport;
e->u.nack.type = type;
memcpy(e->u.nack.iocb, ntfy, sizeof(struct imm_ntfy_from_isp));
return qla2x00_post_work(vha, e);
}
static
void qla2x00_async_nack_sp_done(void *v, void *s, int res)
{
struct scsi_qla_host *vha = (struct scsi_qla_host *)v;
struct srb *sp = (struct srb *)s;
unsigned long flags;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async done-%s res %x %8phC type %d\n",
sp->name, res, sp->fcport->port_name, sp->type);
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
sp->fcport->flags &= ~FCF_ASYNC_SENT;
sp->fcport->chip_reset = vha->hw->chip_reset;
switch (sp->type) {
case SRB_NACK_PLOGI:
sp->fcport->login_gen++;
sp->fcport->fw_login_state = DSC_LS_PLOGI_COMP;
sp->fcport->logout_on_delete = 1;
break;
case SRB_NACK_PRLI:
sp->fcport->fw_login_state = DSC_LS_PRLI_COMP;
sp->fcport->deleted = 0;
if (!sp->fcport->login_succ &&
!IS_SW_RESV_ADDR(sp->fcport->d_id)) {
sp->fcport->login_succ = 1;
vha->fcport_count++;
if (!IS_IIDMA_CAPABLE(vha->hw) ||
!vha->hw->flags.gpsc_supported) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post upd_fcport fcp_cnt %d\n",
__func__, __LINE__,
sp->fcport->port_name,
vha->fcport_count);
qla24xx_post_upd_fcport_work(vha, sp->fcport);
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gpsc fcp_cnt %d\n",
__func__, __LINE__,
sp->fcport->port_name,
vha->fcport_count);
qla24xx_post_gpsc_work(vha, sp->fcport);
}
}
break;
case SRB_NACK_LOGO:
sp->fcport->login_gen++;
sp->fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
qlt_logo_completion_handler(sp->fcport, MBS_COMMAND_COMPLETE);
break;
}
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
sp->free(vha, sp);
}
int qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport,
struct imm_ntfy_from_isp *ntfy, int type)
{
int rval = QLA_FUNCTION_FAILED;
srb_t *sp;
char *c = NULL;
fcport->flags |= FCF_ASYNC_SENT;
switch (type) {
case SRB_NACK_PLOGI:
fcport->fw_login_state = DSC_LS_PLOGI_PEND;
c = "PLOGI";
break;
case SRB_NACK_PRLI:
fcport->fw_login_state = DSC_LS_PRLI_PEND;
c = "PRLI";
break;
case SRB_NACK_LOGO:
fcport->fw_login_state = DSC_LS_LOGO_PEND;
c = "LOGO";
break;
}
sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
if (!sp)
goto done;
sp->type = type;
sp->name = "nack";
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2);
sp->u.iocb_cmd.u.nack.ntfy = ntfy;
sp->done = qla2x00_async_nack_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
ql_dbg(ql_dbg_disc, vha, 0xffff,
"Async-%s %8phC hndl %x %s\n",
sp->name, fcport->port_name, sp->handle, c);
return rval;
done_free_sp:
sp->free(vha, sp);
done:
fcport->flags &= ~FCF_ASYNC_SENT;
return rval;
}
void qla24xx_do_nack_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
{
fc_port_t *t;
unsigned long flags;
switch (e->u.nack.type) {
case SRB_NACK_PRLI:
mutex_lock(&vha->vha_tgt.tgt_mutex);
t = qlt_create_sess(vha, e->u.nack.fcport, 0);
mutex_unlock(&vha->vha_tgt.tgt_mutex);
if (t) {
ql_log(ql_log_info, vha, 0xffff,
"%s create sess success %p", __func__, t);
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
/* create sess has an extra kref */
vha->hw->tgt.tgt_ops->put_sess(e->u.nack.fcport);
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
}
break;
}
qla24xx_async_notify_ack(vha, e->u.nack.fcport,
(struct imm_ntfy_from_isp*)e->u.nack.iocb, e->u.nack.type);
}
void qla24xx_delete_sess_fn(struct work_struct *work)
{
fc_port_t *fcport = container_of(work, struct fc_port, del_work);
struct qla_hw_data *ha = fcport->vha->hw;
unsigned long flags;
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
if (fcport->se_sess) {
ha->tgt.tgt_ops->shutdown_sess(fcport);
ha->tgt.tgt_ops->put_sess(fcport);
} else {
qlt_unreg_sess(fcport);
}
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
}
/*
* Called from qla2x00_reg_remote_port()
*/
void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
{
struct qla_hw_data *ha = vha->hw;
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
struct fc_port *sess = fcport;
unsigned long flags;
if (!vha->hw->tgt.tgt_ops)
return;
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
if (tgt->tgt_stop) {
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return;
}
if (fcport->disc_state == DSC_DELETE_PEND) {
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return;
}
if (!sess->se_sess) {
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
mutex_lock(&vha->vha_tgt.tgt_mutex);
sess = qlt_create_sess(vha, fcport, false);
mutex_unlock(&vha->vha_tgt.tgt_mutex);
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
} else {
if (fcport->fw_login_state == DSC_LS_PRLI_COMP) {
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return;
}
if (!kref_get_unless_zero(&sess->sess_kref)) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s: kref_get fail sess %8phC \n",
__func__, sess->port_name);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return;
}
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c,
"qla_target(%u): %ssession for port %8phC "
"(loop ID %d) reappeared\n", vha->vp_idx,
sess->local ? "local " : "", sess->port_name, sess->loop_id);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007,
"Reappeared sess %p\n", sess);
ha->tgt.tgt_ops->update_sess(sess, fcport->d_id,
fcport->loop_id,
(fcport->flags & FCF_CONF_COMP_SUPPORTED));
}
if (sess && sess->local) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d,
"qla_target(%u): local session for "
"port %8phC (loop ID %d) became global\n", vha->vp_idx,
fcport->port_name, sess->loop_id);
sess->local = 0;
}
ha->tgt.tgt_ops->put_sess(sess);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
}
/* /*
* This is a zero-base ref-counting solution, since hardware_lock * This is a zero-base ref-counting solution, since hardware_lock
...@@ -413,30 +657,51 @@ qlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id, ...@@ -413,30 +657,51 @@ qlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id,
return pla; return pla;
} }
static void qlt_plogi_ack_unref(struct scsi_qla_host *vha, void qlt_plogi_ack_unref(struct scsi_qla_host *vha,
struct qlt_plogi_ack_t *pla) struct qlt_plogi_ack_t *pla)
{ {
struct imm_ntfy_from_isp *iocb = &pla->iocb; struct imm_ntfy_from_isp *iocb = &pla->iocb;
port_id_t port_id;
uint16_t loop_id;
fc_port_t *fcport = pla->fcport;
BUG_ON(!pla->ref_count); BUG_ON(!pla->ref_count);
pla->ref_count--; pla->ref_count--;
if (pla->ref_count) if (pla->ref_count)
return; return;
ql_dbg(ql_dbg_async, vha, 0x5089, ql_dbg(ql_dbg_disc, vha, 0x5089,
"Sending PLOGI ACK to wwn %8phC s_id %02x:%02x:%02x loop_id %#04x" "Sending PLOGI ACK to wwn %8phC s_id %02x:%02x:%02x loop_id %#04x"
" exch %#x ox_id %#x\n", iocb->u.isp24.port_name, " exch %#x ox_id %#x\n", iocb->u.isp24.port_name,
iocb->u.isp24.port_id[2], iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[2], iocb->u.isp24.port_id[1],
iocb->u.isp24.port_id[0], iocb->u.isp24.port_id[0],
le16_to_cpu(iocb->u.isp24.nport_handle), le16_to_cpu(iocb->u.isp24.nport_handle),
iocb->u.isp24.exchange_address, iocb->ox_id); iocb->u.isp24.exchange_address, iocb->ox_id);
qlt_send_notify_ack(vha, iocb, 0, 0, 0, 0, 0, 0);
port_id.b.domain = iocb->u.isp24.port_id[2];
port_id.b.area = iocb->u.isp24.port_id[1];
port_id.b.al_pa = iocb->u.isp24.port_id[0];
port_id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
fcport->loop_id = loop_id;
fcport->d_id = port_id;
qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PLOGI);
list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] == pla)
fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL;
if (fcport->plogi_link[QLT_PLOGI_LINK_CONFLICT] == pla)
fcport->plogi_link[QLT_PLOGI_LINK_CONFLICT] = NULL;
}
list_del(&pla->list); list_del(&pla->list);
kmem_cache_free(qla_tgt_plogi_cachep, pla); kmem_cache_free(qla_tgt_plogi_cachep, pla);
} }
static void void
qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla, qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla,
struct fc_port *sess, enum qlt_plogi_link_t link) struct fc_port *sess, enum qlt_plogi_link_t link)
{ {
...@@ -444,15 +709,19 @@ qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla, ...@@ -444,15 +709,19 @@ qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla,
/* Inc ref_count first because link might already be pointing at pla */ /* Inc ref_count first because link might already be pointing at pla */
pla->ref_count++; pla->ref_count++;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf097,
"Linking sess %p [%d] wwn %8phC with PLOGI ACK to wwn %8phC"
" s_id %02x:%02x:%02x, ref=%d pla %p link %d\n",
sess, link, sess->port_name,
iocb->u.isp24.port_name, iocb->u.isp24.port_id[2],
iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
pla->ref_count, pla, link);
if (sess->plogi_link[link]) if (sess->plogi_link[link])
qlt_plogi_ack_unref(vha, sess->plogi_link[link]); qlt_plogi_ack_unref(vha, sess->plogi_link[link]);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf097, if (link == QLT_PLOGI_LINK_SAME_WWN)
"Linking sess %p [%d] wwn %8phC with PLOGI ACK to wwn %8phC" pla->fcport = sess;
" s_id %02x:%02x:%02x, ref=%d\n", sess, link, sess->port_name,
iocb->u.isp24.port_name, iocb->u.isp24.port_id[2],
iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
pla->ref_count);
sess->plogi_link[link] = pla; sess->plogi_link[link] = pla;
} }
...@@ -512,7 +781,7 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -512,7 +781,7 @@ static void qlt_free_session_done(struct work_struct *work)
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
unsigned long flags; unsigned long flags;
bool logout_started = false; bool logout_started = false;
fc_port_t fcport; struct event_arg ea;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084,
"%s: se_sess %p / sess %p from port %8phC loop_id %#04x" "%s: se_sess %p / sess %p from port %8phC loop_id %#04x"
...@@ -522,8 +791,8 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -522,8 +791,8 @@ static void qlt_free_session_done(struct work_struct *work)
sess->logout_on_delete, sess->keep_nport_handle, sess->logout_on_delete, sess->keep_nport_handle,
sess->send_els_logo); sess->send_els_logo);
BUG_ON(!tgt);
if (!IS_SW_RESV_ADDR(sess->d_id)) {
if (sess->send_els_logo) { if (sess->send_els_logo) {
qlt_port_logo_t logo; qlt_port_logo_t logo;
logo.id = sess->d_id; logo.id = sess->d_id;
...@@ -533,14 +802,7 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -533,14 +802,7 @@ static void qlt_free_session_done(struct work_struct *work)
if (sess->logout_on_delete) { if (sess->logout_on_delete) {
int rc; int rc;
rc = qla2x00_post_async_logout_work(vha, sess, NULL);
memset(&fcport, 0, sizeof(fcport));
fcport.loop_id = sess->loop_id;
fcport.d_id = sess->d_id;
memcpy(fcport.port_name, sess->port_name, WWN_SIZE);
fcport.vha = vha;
rc = qla2x00_post_async_logout_work(vha, &fcport, NULL);
if (rc != QLA_SUCCESS) if (rc != QLA_SUCCESS)
ql_log(ql_log_warn, vha, 0xf085, ql_log(ql_log_warn, vha, 0xf085,
"Schedule logo failed sess %p rc %d\n", "Schedule logo failed sess %p rc %d\n",
...@@ -548,6 +810,7 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -548,6 +810,7 @@ static void qlt_free_session_done(struct work_struct *work)
else else
logout_started = true; logout_started = true;
} }
}
/* /*
* Release the target session for FC Nexus from fabric module code. * Release the target session for FC Nexus from fabric module code.
...@@ -568,12 +831,38 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -568,12 +831,38 @@ static void qlt_free_session_done(struct work_struct *work)
msleep(100); msleep(100);
} }
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf087, ql_dbg(ql_dbg_disc, vha, 0xf087,
"%s: sess %p logout completed\n", "%s: sess %p logout completed\n",
__func__, sess); __func__, sess);
} }
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->tgt.sess_lock, flags);
if (sess->se_sess) {
sess->se_sess = NULL;
if (tgt && !IS_SW_RESV_ADDR(sess->d_id))
tgt->sess_count--;
}
sess->disc_state = DSC_DELETED;
sess->fw_login_state = DSC_LS_PORT_UNAVAIL;
sess->deleted = QLA_SESS_DELETED;
sess->login_retry = vha->hw->login_retry_count;
if (sess->login_succ && !IS_SW_RESV_ADDR(sess->d_id)) {
vha->fcport_count--;
sess->login_succ = 0;
}
if (sess->chip_reset != sess->vha->hw->chip_reset)
qla2x00_clear_loop_id(sess);
if (sess->conflict) {
sess->conflict->login_pause = 0;
sess->conflict = NULL;
if (!test_bit(UNLOADING, &vha->dpc_flags))
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
}
{ {
struct qlt_plogi_ack_t *own = struct qlt_plogi_ack_t *own =
sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]; sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN];
...@@ -591,6 +880,7 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -591,6 +880,7 @@ static void qlt_free_session_done(struct work_struct *work)
own ? own->ref_count : -1, own ? own->ref_count : -1,
iocb->u.isp24.port_name, con->ref_count); iocb->u.isp24.port_name, con->ref_count);
qlt_plogi_ack_unref(vha, con); qlt_plogi_ack_unref(vha, con);
sess->plogi_link[QLT_PLOGI_LINK_CONFLICT] = NULL;
} else { } else {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09a, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09a,
"se_sess %p / sess %p port %8phC is gone, %s (ref=%d)\n", "se_sess %p / sess %p port %8phC is gone, %s (ref=%d)\n",
...@@ -600,25 +890,30 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -600,25 +890,30 @@ static void qlt_free_session_done(struct work_struct *work)
own ? own->ref_count : -1); own ? own->ref_count : -1);
} }
if (own) if (own) {
sess->fw_login_state = DSC_LS_PLOGI_PEND;
qlt_plogi_ack_unref(vha, own); qlt_plogi_ack_unref(vha, own);
sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL;
}
} }
spin_unlock_irqrestore(&ha->hardware_lock, flags);
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
sess->se_sess = NULL;
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001,
"Unregistration of sess %p finished\n", sess); "Unregistration of sess %p %8phC finished fcp_cnt %d\n",
sess, sess->port_name, vha->fcport_count);
/* if (tgt && (tgt->sess_count == 0))
* We need to protect against race, when tgt is freed before or
* inside wake_up()
*/
tgt->sess_count--;
if (tgt->sess_count == 0)
wake_up_all(&tgt->waitQ); wake_up_all(&tgt->waitQ);
if (vha->fcport_count == 0)
wake_up_all(&vha->fcport_waitQ);
if (!tgt || !tgt->tgt_stop) {
memset(&ea, 0, sizeof(ea));
ea.event = FCME_DELETE_DONE;
ea.fcport = sess;
qla2x00_fcport_event_handler(vha, &ea);
}
} }
/* ha->tgt.sess_lock supposed to be held on entry */ /* ha->tgt.sess_lock supposed to be held on entry */
...@@ -636,6 +931,9 @@ void qlt_unreg_sess(struct fc_port *sess) ...@@ -636,6 +931,9 @@ void qlt_unreg_sess(struct fc_port *sess)
qla2x00_mark_device_lost(vha, sess, 1, 1); qla2x00_mark_device_lost(vha, sess, 1, 1);
sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
sess->disc_state = DSC_DELETE_PEND;
sess->last_rscn_gen = sess->rscn_gen;
sess->last_login_gen = sess->login_gen;
INIT_WORK(&sess->free_work, qlt_free_session_done); INIT_WORK(&sess->free_work, qlt_free_session_done);
schedule_work(&sess->free_work); schedule_work(&sess->free_work);
...@@ -679,48 +977,57 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) ...@@ -679,48 +977,57 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
return qlt_issue_task_mgmt(sess, 0, mcmd, iocb, QLA24XX_MGMT_SEND_NACK); return qlt_issue_task_mgmt(sess, 0, mcmd, iocb, QLA24XX_MGMT_SEND_NACK);
} }
static void qla24xx_chk_fcp_state(struct fc_port *sess)
{
if (sess->chip_reset != sess->vha->hw->chip_reset) {
sess->logout_on_delete = 0;
sess->logo_ack_needed = 0;
sess->fw_login_state = DSC_LS_PORT_UNAVAIL;
sess->scan_state = 0;
}
}
/* ha->tgt.sess_lock supposed to be held on entry */ /* ha->tgt.sess_lock supposed to be held on entry */
static void qlt_schedule_sess_for_deletion(struct fc_port *sess, void qlt_schedule_sess_for_deletion(struct fc_port *sess,
bool immediate) bool immediate)
{ {
struct qla_tgt *tgt = sess->tgt; struct qla_tgt *tgt = sess->tgt;
uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5;
if (sess->deleted) { if (sess->disc_state == DSC_DELETE_PEND)
/* Upgrade to unconditional deletion in case it was temporary */ return;
if (immediate && sess->deleted == QLA_SESS_DELETION_PENDING)
list_del(&sess->del_list_entry); if (sess->disc_state == DSC_DELETED) {
else if (tgt && tgt->tgt_stop && (tgt->sess_count == 0))
wake_up_all(&tgt->waitQ);
if (sess->vha->fcport_count == 0)
wake_up_all(&sess->vha->fcport_waitQ);
if (!sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] &&
!sess->plogi_link[QLT_PLOGI_LINK_CONFLICT])
return; return;
} }
ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, sess->disc_state = DSC_DELETE_PEND;
"Scheduling sess %p for deletion\n", sess);
if (immediate) { if (sess->deleted == QLA_SESS_DELETED)
dev_loss_tmo = 0; sess->logout_on_delete = 0;
sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
list_add(&sess->del_list_entry, &tgt->del_sess_list); sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
} else { qla24xx_chk_fcp_state(sess);
sess->deleted = QLA_SESS_DELETION_PENDING;
list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
}
sess->expires = jiffies + dev_loss_tmo * HZ; ql_dbg(ql_dbg_tgt, sess->vha, 0xe001,
"Scheduling sess %p for deletion\n", sess);
ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, schedule_work(&sess->del_work);
"qla_target(%d): session for port %8phC (loop ID %d s_id %02x:%02x:%02x)" }
" scheduled for deletion in %u secs (expires: %lu) immed: %d, logout: %d, gen: %#x\n",
sess->vha->vp_idx, sess->port_name, sess->loop_id,
sess->d_id.b.domain, sess->d_id.b.area, sess->d_id.b.al_pa,
dev_loss_tmo, sess->expires, immediate, sess->logout_on_delete,
sess->generation);
if (immediate) void qlt_schedule_sess_for_deletion_lock(struct fc_port *sess)
mod_delayed_work(system_wq, &tgt->sess_del_work, 0); {
else unsigned long flags;
schedule_delayed_work(&tgt->sess_del_work, struct qla_hw_data *ha = sess->vha->hw;
sess->expires - jiffies); spin_lock_irqsave(&ha->tgt.sess_lock, flags);
qlt_schedule_sess_for_deletion(sess, 1);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
} }
/* ha->tgt.sess_lock supposed to be held on entry */ /* ha->tgt.sess_lock supposed to be held on entry */
...@@ -731,7 +1038,7 @@ static void qlt_clear_tgt_db(struct qla_tgt *tgt) ...@@ -731,7 +1038,7 @@ static void qlt_clear_tgt_db(struct qla_tgt *tgt)
list_for_each_entry(sess, &vha->vp_fcports, list) { list_for_each_entry(sess, &vha->vp_fcports, list) {
if (sess->se_sess) if (sess->se_sess)
qlt_schedule_sess_for_deletion(sess, true); qlt_schedule_sess_for_deletion(sess, 1);
} }
/* At this point tgt could be already dead */ /* At this point tgt could be already dead */
...@@ -786,49 +1093,6 @@ static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id, ...@@ -786,49 +1093,6 @@ static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id,
return res; return res;
} }
/* ha->tgt.sess_lock supposed to be held on entry */
static void qlt_undelete_sess(struct fc_port *sess)
{
BUG_ON(sess->deleted != QLA_SESS_DELETION_PENDING);
list_del_init(&sess->del_list_entry);
sess->deleted = 0;
}
static void qlt_del_sess_work_fn(struct delayed_work *work)
{
struct qla_tgt *tgt = container_of(work, struct qla_tgt,
sess_del_work);
struct scsi_qla_host *vha = tgt->vha;
struct qla_hw_data *ha = vha->hw;
struct fc_port *sess;
unsigned long flags, elapsed;
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
while (!list_empty(&tgt->del_sess_list)) {
sess = list_entry(tgt->del_sess_list.next, typeof(*sess),
del_list_entry);
elapsed = jiffies;
if (time_after_eq(elapsed, sess->expires)) {
/* No turning back */
list_del_init(&sess->del_list_entry);
sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
"Timeout: sess %p about to be deleted\n",
sess);
if (sess->se_sess)
ha->tgt.tgt_ops->shutdown_sess(sess);
ha->tgt.tgt_ops->put_sess(sess);
} else {
schedule_delayed_work(&tgt->sess_del_work,
sess->expires - elapsed);
break;
}
}
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
}
/* /*
* Adds an extra ref to allow to drop hw lock after adding sess to the list. * Adds an extra ref to allow to drop hw lock after adding sess to the list.
* Caller must put it. * Caller must put it.
...@@ -839,93 +1103,65 @@ static struct fc_port *qlt_create_sess( ...@@ -839,93 +1103,65 @@ static struct fc_port *qlt_create_sess(
bool local) bool local)
{ {
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct fc_port *sess; struct fc_port *sess = fcport;
unsigned long flags; unsigned long flags;
/* Check to avoid double sessions */ if (vha->vha_tgt.qla_tgt->tgt_stop)
spin_lock_irqsave(&ha->tgt.sess_lock, flags); return NULL;
list_for_each_entry(sess, &vha->vp_fcports, list) {
if (!memcmp(sess->port_name, fcport->port_name, WWN_SIZE)) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf005,
"Double sess %p found (s_id %x:%x:%x, "
"loop_id %d), updating to d_id %x:%x:%x, "
"loop_id %d", sess, sess->d_id.b.domain,
sess->d_id.b.al_pa, sess->d_id.b.area,
sess->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.al_pa, fcport->d_id.b.area,
fcport->loop_id);
/* Cannot undelete at this point */
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
spin_unlock_irqrestore(&ha->tgt.sess_lock,
flags);
return NULL;
}
if (sess->deleted)
qlt_undelete_sess(sess);
if (!sess->se_sess) {
if (ha->tgt.tgt_ops->check_initiator_node_acl(vha,
&sess->port_name[0], sess) < 0) {
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return NULL;
}
}
kref_get(&sess->sess_kref);
ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, fcport->loop_id,
(fcport->flags & FCF_CONF_COMP_SUPPORTED));
if (sess->local && !local)
sess->local = 0;
qlt_do_generation_tick(vha, &sess->generation);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return sess; if (fcport->se_sess) {
if (!kref_get_unless_zero(&sess->sess_kref)) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s: kref_get_unless_zero failed for %8phC\n",
__func__, sess->port_name);
return NULL;
} }
} return fcport;
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
sess = kzalloc(sizeof(*sess), GFP_KERNEL);
if (!sess) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04a,
"qla_target(%u): session allocation failed, all commands "
"from port %8phC will be refused", vha->vp_idx,
fcport->port_name);
return NULL;
} }
sess->tgt = vha->vha_tgt.qla_tgt; sess->tgt = vha->vha_tgt.qla_tgt;
sess->vha = vha;
sess->d_id = fcport->d_id;
sess->loop_id = fcport->loop_id;
sess->local = local; sess->local = local;
kref_init(&sess->sess_kref);
INIT_LIST_HEAD(&sess->del_list_entry);
/* Under normal circumstances we want to logout from firmware when /*
* Under normal circumstances we want to logout from firmware when
* session eventually ends and release corresponding nport handle. * session eventually ends and release corresponding nport handle.
* In the exception cases (e.g. when new PLOGI is waiting) corresponding * In the exception cases (e.g. when new PLOGI is waiting) corresponding
* code will adjust these flags as necessary. */ * code will adjust these flags as necessary.
*/
sess->logout_on_delete = 1; sess->logout_on_delete = 1;
sess->keep_nport_handle = 0; sess->keep_nport_handle = 0;
sess->logout_completed = 0;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, if (ha->tgt.tgt_ops->check_initiator_node_acl(vha,
"Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", &fcport->port_name[0], sess) < 0) {
sess, vha->vha_tgt.qla_tgt); ql_dbg(ql_dbg_tgt_mgt, vha, 0xffff,
"(%d) %8phC check_initiator_node_acl failed\n",
vha->vp_idx, fcport->port_name);
return NULL;
} else {
kref_init(&fcport->sess_kref);
/*
* Take an extra reference to ->sess_kref here to handle
* fc_port access across ->tgt.sess_lock reaquire.
*/
if (!kref_get_unless_zero(&sess->sess_kref)) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s: kref_get_unless_zero failed for %8phC\n",
__func__, sess->port_name);
return NULL;
}
sess->conf_compl_supported = (fcport->flags & FCF_CONF_COMP_SUPPORTED); spin_lock_irqsave(&ha->tgt.sess_lock, flags);
BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name)); if (!IS_SW_RESV_ADDR(sess->d_id))
memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name)); vha->vha_tgt.qla_tgt->sess_count++;
spin_lock_irqsave(&ha->tgt.sess_lock, flags); qlt_do_generation_tick(vha, &sess->generation);
list_add_tail(&sess->list, &vha->vp_fcports); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
vha->vha_tgt.qla_tgt->sess_count++; }
qlt_do_generation_tick(vha, &sess->generation);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006,
"Adding sess %p se_sess %p to tgt %p sess_count %d\n",
sess, sess->se_sess, vha->vha_tgt.qla_tgt,
vha->vha_tgt.qla_tgt->sess_count);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
"qla_target(%d): %ssession for wwn %8phC (loop_id %d, " "qla_target(%d): %ssession for wwn %8phC (loop_id %d, "
...@@ -934,23 +1170,6 @@ static struct fc_port *qlt_create_sess( ...@@ -934,23 +1170,6 @@ static struct fc_port *qlt_create_sess(
fcport->loop_id, sess->d_id.b.domain, sess->d_id.b.area, fcport->loop_id, sess->d_id.b.domain, sess->d_id.b.area,
sess->d_id.b.al_pa, sess->conf_compl_supported ? "" : "not "); sess->d_id.b.al_pa, sess->conf_compl_supported ? "" : "not ");
/*
* Determine if this fc_port->port_name is allowed to access
* target mode using explict NodeACLs+MappedLUNs, or using
* TPG demo mode. If this is successful a target mode FC nexus
* is created.
*/
if (ha->tgt.tgt_ops->check_initiator_node_acl(vha,
&fcport->port_name[0], sess) < 0) {
return NULL;
} else {
/*
* Take an extra reference to ->sess_kref here to handle fc_port
* access across ->tgt.sess_lock reaquire.
*/
kref_get(&sess->sess_kref);
}
return sess; return sess;
} }
...@@ -1007,12 +1226,12 @@ static inline int test_tgt_sess_count(struct qla_tgt *tgt) ...@@ -1007,12 +1226,12 @@ static inline int test_tgt_sess_count(struct qla_tgt *tgt)
* We need to protect against race, when tgt is freed before or * We need to protect against race, when tgt is freed before or
* inside wake_up() * inside wake_up()
*/ */
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->tgt.sess_lock, flags);
ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002, ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002,
"tgt %p, sess_count=%d\n", "tgt %p, sess_count=%d\n",
tgt, tgt->sess_count); tgt, tgt->sess_count);
res = (tgt->sess_count == 0); res = (tgt->sess_count == 0);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
return res; return res;
} }
...@@ -1060,8 +1279,6 @@ int qlt_stop_phase1(struct qla_tgt *tgt) ...@@ -1060,8 +1279,6 @@ int qlt_stop_phase1(struct qla_tgt *tgt)
mutex_unlock(&vha->vha_tgt.tgt_mutex); mutex_unlock(&vha->vha_tgt.tgt_mutex);
mutex_unlock(&qla_tgt_mutex); mutex_unlock(&qla_tgt_mutex);
flush_delayed_work(&tgt->sess_del_work);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009,
"Waiting for sess works (tgt %p)", tgt); "Waiting for sess works (tgt %p)", tgt);
spin_lock_irqsave(&tgt->sess_work_lock, flags); spin_lock_irqsave(&tgt->sess_work_lock, flags);
...@@ -1205,6 +1422,7 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, ...@@ -1205,6 +1422,7 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha,
nack = (struct nack_to_isp *)pkt; nack = (struct nack_to_isp *)pkt;
nack->ox_id = ntfy->ox_id; nack->ox_id = ntfy->ox_id;
nack->u.isp24.handle = QLA_TGT_SKIP_HANDLE;
nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle; nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) { if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
nack->u.isp24.flags = ntfy->u.isp24.flags & nack->u.isp24.flags = ntfy->u.isp24.flags &
...@@ -1552,7 +1770,7 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, ...@@ -1552,7 +1770,7 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha,
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { if (sess->deleted) {
qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false); qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
return; return;
} }
...@@ -1650,10 +1868,19 @@ void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd) ...@@ -1650,10 +1868,19 @@ void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd)
return; return;
} }
if (mcmd->flags == QLA24XX_MGMT_SEND_NACK) if (mcmd->flags == QLA24XX_MGMT_SEND_NACK) {
qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy, if (mcmd->orig_iocb.imm_ntfy.u.isp24.status_subcode ==
0, 0, 0, 0, 0, 0); ELS_LOGO) {
else { ql_dbg(ql_dbg_disc, vha, 0xffff,
"TM response logo %phC status %#x state %#x",
mcmd->sess->port_name, mcmd->fc_tm_rsp,
mcmd->flags);
qlt_schedule_sess_for_deletion_lock(mcmd->sess);
} else {
qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy,
0, 0, 0, 0, 0, 0);
}
} else {
if (mcmd->orig_iocb.atio.u.raw.entry_type == ABTS_RECV_24XX) if (mcmd->orig_iocb.atio.u.raw.entry_type == ABTS_RECV_24XX)
qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts, qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts,
mcmd->fc_tm_rsp, false); mcmd->fc_tm_rsp, false);
...@@ -2470,7 +2697,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, ...@@ -2470,7 +2697,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
int res; int res;
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
if (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { if (cmd->sess && cmd->sess->deleted) {
cmd->state = QLA_TGT_STATE_PROCESSED; cmd->state = QLA_TGT_STATE_PROCESSED;
if (cmd->sess->logout_completed) if (cmd->sess->logout_completed)
/* no need to terminate. FW already freed exchange. */ /* no need to terminate. FW already freed exchange. */
...@@ -2645,7 +2872,7 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) ...@@ -2645,7 +2872,7 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
if (!vha->flags.online || (cmd->reset_count != ha->chip_reset) || if (!vha->flags.online || (cmd->reset_count != ha->chip_reset) ||
(cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS)) { (cmd->sess && cmd->sess->deleted)) {
/* /*
* Either the port is not online or this request was from * Either the port is not online or this request was from
* previous life, just abort the processing. * previous life, just abort the processing.
...@@ -3345,7 +3572,11 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, ...@@ -3345,7 +3572,11 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
*/ */
cmd->sess->logout_on_delete = 0; cmd->sess->logout_on_delete = 0;
cmd->sess->send_els_logo = 1; cmd->sess->send_els_logo = 1;
qlt_schedule_sess_for_deletion(cmd->sess, true); ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__, cmd->sess->port_name);
qlt_schedule_sess_for_deletion_lock(cmd->sess);
} }
break; break;
} }
...@@ -3649,6 +3880,7 @@ static void qlt_create_sess_from_atio(struct work_struct *work) ...@@ -3649,6 +3880,7 @@ static void qlt_create_sess_from_atio(struct work_struct *work)
kfree(op); kfree(op);
return; return;
} }
/* /*
* __qlt_do_work() will call qlt_put_sess() to release * __qlt_do_work() will call qlt_put_sess() to release
* the extra reference taken above by qlt_make_local_sess() * the extra reference taken above by qlt_make_local_sess()
...@@ -3656,13 +3888,11 @@ static void qlt_create_sess_from_atio(struct work_struct *work) ...@@ -3656,13 +3888,11 @@ static void qlt_create_sess_from_atio(struct work_struct *work)
__qlt_do_work(cmd); __qlt_do_work(cmd);
kfree(op); kfree(op);
return; return;
out_term: out_term:
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
qlt_send_term_exchange(vha, NULL, &op->atio, 1, 0); qlt_send_term_exchange(vha, NULL, &op->atio, 1, 0);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
kfree(op); kfree(op);
} }
/* ha->hardware_lock supposed to be held on entry */ /* ha->hardware_lock supposed to be held on entry */
...@@ -3702,7 +3932,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, ...@@ -3702,7 +3932,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
/* Another WWN used to have our s_id. Our PLOGI scheduled its /* Another WWN used to have our s_id. Our PLOGI scheduled its
* session deletion, but it's still in sess_del_work wq */ * session deletion, but it's still in sess_del_work wq */
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { if (sess->deleted) {
ql_dbg(ql_dbg_io, vha, 0x3061, ql_dbg(ql_dbg_io, vha, 0x3061,
"New command while old session %p is being deleted\n", "New command while old session %p is being deleted\n",
sess); sess);
...@@ -3712,7 +3942,13 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, ...@@ -3712,7 +3942,13 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
/* /*
* Do kref_get() before returning + dropping qla_hw_data->hardware_lock. * Do kref_get() before returning + dropping qla_hw_data->hardware_lock.
*/ */
kref_get(&sess->sess_kref); if (!kref_get_unless_zero(&sess->sess_kref)) {
ql_dbg(ql_dbg_tgt, vha, 0xffff,
"%s: kref_get fail, %8phC oxid %x \n",
__func__, sess->port_name,
be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id));
return -EFAULT;
}
cmd = qlt_get_tag(vha, sess, atio); cmd = qlt_get_tag(vha, sess, atio);
if (!cmd) { if (!cmd) {
...@@ -3729,9 +3965,9 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, ...@@ -3729,9 +3965,9 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
cmd->se_cmd.cpuid = ha->msix_count ? cmd->se_cmd.cpuid = ha->msix_count ?
ha->tgt.rspq_vector_cpuid : WORK_CPU_UNBOUND; ha->tgt.rspq_vector_cpuid : WORK_CPU_UNBOUND;
spin_lock(&vha->cmd_list_lock); spin_lock_irqsave(&vha->cmd_list_lock, flags);
list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list); list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list);
spin_unlock(&vha->cmd_list_lock); spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
INIT_WORK(&cmd->work, qlt_do_work); INIT_WORK(&cmd->work, qlt_do_work);
if (ha->msix_count) { if (ha->msix_count) {
...@@ -3826,7 +4062,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) ...@@ -3826,7 +4062,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb)
sizeof(struct atio_from_isp)); sizeof(struct atio_from_isp));
} }
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) if (sess->deleted)
return -EFAULT; return -EFAULT;
return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
...@@ -3901,22 +4137,20 @@ static int qlt_abort_task(struct scsi_qla_host *vha, ...@@ -3901,22 +4137,20 @@ static int qlt_abort_task(struct scsi_qla_host *vha,
void qlt_logo_completion_handler(fc_port_t *fcport, int rc) void qlt_logo_completion_handler(fc_port_t *fcport, int rc)
{ {
if (fcport->tgt_session) { if (rc != MBS_COMMAND_COMPLETE) {
if (rc != MBS_COMMAND_COMPLETE) { ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093,
ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093, "%s: se_sess %p / sess %p from"
"%s: se_sess %p / sess %p from" " port %8phC loop_id %#04x s_id %02x:%02x:%02x"
" port %8phC loop_id %#04x s_id %02x:%02x:%02x" " LOGO failed: %#x\n",
" LOGO failed: %#x\n", __func__,
__func__, fcport->se_sess,
fcport->se_sess, fcport,
fcport->tgt_session, fcport->port_name, fcport->loop_id,
fcport->port_name, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, rc);
fcport->d_id.b.al_pa, rc);
}
fcport->logout_completed = 1;
} }
fcport->logout_completed = 1;
} }
/* /*
...@@ -3926,13 +4160,12 @@ void qlt_logo_completion_handler(fc_port_t *fcport, int rc) ...@@ -3926,13 +4160,12 @@ void qlt_logo_completion_handler(fc_port_t *fcport, int rc)
* deletion. Returns existing session with matching wwn if present. * deletion. Returns existing session with matching wwn if present.
* Null otherwise. * Null otherwise.
*/ */
static struct fc_port * struct fc_port *
qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, qlt_find_sess_invalidate_other(scsi_qla_host_t *vha, uint64_t wwn,
port_id_t port_id, uint16_t loop_id, struct fc_port **conflict_sess) port_id_t port_id, uint16_t loop_id, struct fc_port **conflict_sess)
{ {
struct fc_port *sess = NULL, *other_sess; struct fc_port *sess = NULL, *other_sess;
uint64_t other_wwn; uint64_t other_wwn;
scsi_qla_host_t *vha = tgt->vha;
*conflict_sess = NULL; *conflict_sess = NULL;
...@@ -3949,7 +4182,7 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, ...@@ -3949,7 +4182,7 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn,
/* find other sess with nport_id collision */ /* find other sess with nport_id collision */
if (port_id.b24 == other_sess->d_id.b24) { if (port_id.b24 == other_sess->d_id.b24) {
if (loop_id != other_sess->loop_id) { if (loop_id != other_sess->loop_id) {
ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000c, ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000c,
"Invalidating sess %p loop_id %d wwn %llx.\n", "Invalidating sess %p loop_id %d wwn %llx.\n",
other_sess, other_sess->loop_id, other_wwn); other_sess, other_sess->loop_id, other_wwn);
...@@ -3965,6 +4198,11 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, ...@@ -3965,6 +4198,11 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn,
* Another wwn used to have our s_id/loop_id * Another wwn used to have our s_id/loop_id
* kill the session, but don't free the loop_id * kill the session, but don't free the loop_id
*/ */
ql_dbg(ql_dbg_tgt_tmr, vha, 0xffff,
"Invalidating sess %p loop_id %d wwn %llx.\n",
other_sess, other_sess->loop_id, other_wwn);
other_sess->keep_nport_handle = 1; other_sess->keep_nport_handle = 1;
*conflict_sess = other_sess; *conflict_sess = other_sess;
qlt_schedule_sess_for_deletion(other_sess, qlt_schedule_sess_for_deletion(other_sess,
...@@ -3974,8 +4212,9 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, ...@@ -3974,8 +4212,9 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn,
} }
/* find other sess with nport handle collision */ /* find other sess with nport handle collision */
if (loop_id == other_sess->loop_id) { if ((loop_id == other_sess->loop_id) &&
ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000d, (loop_id != FC_NO_LOOP_ID)) {
ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000d,
"Invalidating sess %p loop_id %d wwn %llx.\n", "Invalidating sess %p loop_id %d wwn %llx.\n",
other_sess, other_sess->loop_id, other_wwn); other_sess, other_sess->loop_id, other_wwn);
...@@ -4046,9 +4285,12 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4046,9 +4285,12 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
loop_id = le16_to_cpu(iocb->u.isp24.nport_handle); loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, ql_dbg(ql_dbg_disc, vha, 0xf026,
"qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n", "qla_target(%d): Port ID: %02x:%02x:%02x ELS opcode: 0x%02x lid %d %8phC\n",
vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode); vha->vp_idx, iocb->u.isp24.port_id[2],
iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
iocb->u.isp24.status_subcode, loop_id,
iocb->u.isp24.port_name);
/* res = 1 means ack at the end of thread /* res = 1 means ack at the end of thread
* res = 0 means ack async/later. * res = 0 means ack async/later.
...@@ -4061,12 +4303,12 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4061,12 +4303,12 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
if (wwn) { if (wwn) {
spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
sess = qlt_find_sess_invalidate_other(tgt, wwn, sess = qlt_find_sess_invalidate_other(vha, wwn,
port_id, loop_id, &conflict_sess); port_id, loop_id, &conflict_sess);
spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
} }
if (IS_SW_RESV_ADDR(port_id) || (!sess && !conflict_sess)) { if (IS_SW_RESV_ADDR(port_id)) {
res = 1; res = 1;
break; break;
} }
...@@ -4074,42 +4316,66 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4074,42 +4316,66 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
pla = qlt_plogi_ack_find_add(vha, &port_id, iocb); pla = qlt_plogi_ack_find_add(vha, &port_id, iocb);
if (!pla) { if (!pla) {
qlt_send_term_imm_notif(vha, iocb, 1); qlt_send_term_imm_notif(vha, iocb, 1);
res = 0;
break; break;
} }
res = 0; res = 0;
if (conflict_sess) if (conflict_sess) {
conflict_sess->login_gen++;
qlt_plogi_ack_link(vha, pla, conflict_sess, qlt_plogi_ack_link(vha, pla, conflict_sess,
QLT_PLOGI_LINK_CONFLICT); QLT_PLOGI_LINK_CONFLICT);
}
if (!sess) if (!sess) {
pla->ref_count++;
qla24xx_post_newsess_work(vha, &port_id,
iocb->u.isp24.port_name, pla);
res = 0;
break; break;
}
qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN); qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN);
/* sess->fw_login_state = DSC_LS_PLOGI_PEND;
* Under normal circumstances we want to release nport handle sess->d_id = port_id;
* during LOGO process to avoid nport handle leaks inside FW. sess->login_gen++;
* The exception is when LOGO is done while another PLOGI with
* the same nport handle is waiting as might be the case here. switch (sess->disc_state) {
* Note: there is always a possibily of a race where session case DSC_DELETED:
* deletion has already started for other reasons (e.g. ACL qlt_plogi_ack_unref(vha, pla);
* removal) and now PLOGI arrives: break;
* 1. if PLOGI arrived in FW after nport handle has been freed,
* FW must have assigned this PLOGI a new/same handle and we default:
* can proceed ACK'ing it as usual when session deletion /*
* completes. * Under normal circumstances we want to release nport handle
* 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT * during LOGO process to avoid nport handle leaks inside FW.
* bit reached it, the handle has now been released. We'll * The exception is when LOGO is done while another PLOGI with
* get an error when we ACK this PLOGI. Nothing will be sent * the same nport handle is waiting as might be the case here.
* back to initiator. Initiator should eventually retry * Note: there is always a possibily of a race where session
* PLOGI and situation will correct itself. * deletion has already started for other reasons (e.g. ACL
*/ * removal) and now PLOGI arrives:
sess->keep_nport_handle = ((sess->loop_id == loop_id) && * 1. if PLOGI arrived in FW after nport handle has been freed,
(sess->d_id.b24 == port_id.b24)); * FW must have assigned this PLOGI a new/same handle and we
qlt_schedule_sess_for_deletion(sess, true); * can proceed ACK'ing it as usual when session deletion
* completes.
* 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT
* bit reached it, the handle has now been released. We'll
* get an error when we ACK this PLOGI. Nothing will be sent
* back to initiator. Initiator should eventually retry
* PLOGI and situation will correct itself.
*/
sess->keep_nport_handle = ((sess->loop_id == loop_id) &&
(sess->d_id.b24 == port_id.b24));
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post del sess\n",
__func__, __LINE__, sess->port_name);
qlt_schedule_sess_for_deletion_lock(sess);
break;
}
break; break;
case ELS_PRLI: case ELS_PRLI:
...@@ -4117,8 +4383,8 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4117,8 +4383,8 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
if (wwn) { if (wwn) {
spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
sess = qlt_find_sess_invalidate_other(tgt, wwn, port_id, sess = qlt_find_sess_invalidate_other(vha, wwn, port_id,
loop_id, &conflict_sess); loop_id, &conflict_sess);
spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
} }
...@@ -4132,7 +4398,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4132,7 +4398,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
} }
if (sess != NULL) { if (sess != NULL) {
if (sess->deleted) { if (sess->fw_login_state == DSC_LS_PLOGI_PEND) {
/* /*
* Impatient initiator sent PRLI before last * Impatient initiator sent PRLI before last
* PLOGI could finish. Will force him to re-try, * PLOGI could finish. Will force him to re-try,
...@@ -4157,10 +4423,15 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4157,10 +4423,15 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
sess->local = 0; sess->local = 0;
sess->loop_id = loop_id; sess->loop_id = loop_id;
sess->d_id = port_id; sess->d_id = port_id;
sess->fw_login_state = DSC_LS_PRLI_PEND;
if (wd3_lo & BIT_7) if (wd3_lo & BIT_7)
sess->conf_compl_supported = 1; sess->conf_compl_supported = 1;
if ((wd3_lo & BIT_4) == 0)
sess->port_type = FCT_INITIATOR;
else
sess->port_type = FCT_TARGET;
} }
res = 1; /* send notify ack */ res = 1; /* send notify ack */
...@@ -4170,15 +4441,50 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4170,15 +4441,50 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
qla2xxx_wake_dpc(vha); qla2xxx_wake_dpc(vha);
} else { } else {
/* todo: else - create sess here. */ if (sess) {
res = 1; /* send notify ack */ ql_dbg(ql_dbg_disc, vha, 0xffff,
} "%s %d %8phC post nack\n",
__func__, __LINE__, sess->port_name);
qla24xx_post_nack_work(vha, sess, iocb,
SRB_NACK_PRLI);
res = 0;
}
}
break; break;
case ELS_LOGO: case ELS_LOGO:
case ELS_PRLO: case ELS_PRLO:
spin_lock_irqsave(&ha->tgt.sess_lock, flags);
sess = qla2x00_find_fcport_by_loopid(vha, loop_id);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
if (sess) {
sess->login_gen++;
sess->fw_login_state = DSC_LS_LOGO_PEND;
sess->logout_on_delete = 0;
sess->logo_ack_needed = 1;
memcpy(sess->iocb, iocb, IOCB_SIZE);
}
res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s: logo %llx res %d sess %p ",
__func__, wwn, res, sess);
if (res == 0) {
/* cmd went up to ULP. look for qlt_xmit_tm_rsp()
for LOGO_ACK */
BUG_ON(!sess);
res = 0;
} else {
/* cmd did not go upstair. */
if (sess) {
qlt_schedule_sess_for_deletion_lock(sess);
res = 0;
}
/* else logo will be ack */
}
break; break;
case ELS_PDISC: case ELS_PDISC:
case ELS_ADISC: case ELS_ADISC:
...@@ -4189,6 +4495,16 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -4189,6 +4495,16 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
0, 0, 0, 0, 0, 0); 0, 0, 0, 0, 0, 0);
tgt->link_reinit_iocb_pending = 0; tgt->link_reinit_iocb_pending = 0;
} }
sess = qla2x00_find_fcport_by_wwpn(vha,
iocb->u.isp24.port_name, 1);
if (sess) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"sess %p lid %d|%d DS %d LS %d\n",
sess, sess->loop_id, loop_id,
sess->disc_state, sess->fw_login_state);
}
res = 1; /* send notify ack */ res = 1; /* send notify ack */
break; break;
} }
...@@ -4966,7 +5282,6 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, ...@@ -4966,7 +5282,6 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f,
"Async MB 2: Port Logged Out\n"); "Async MB 2: Port Logged Out\n");
break; break;
default: default:
break; break;
} }
...@@ -4977,8 +5292,10 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, ...@@ -4977,8 +5292,10 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha,
uint16_t loop_id) uint16_t loop_id)
{ {
fc_port_t *fcport; fc_port_t *fcport, *tfcp, *del;
int rc; int rc;
unsigned long flags;
u8 newfcport = 0;
fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
if (!fcport) { if (!fcport) {
...@@ -5000,6 +5317,59 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, ...@@ -5000,6 +5317,59 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha,
return NULL; return NULL;
} }
del = NULL;
spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
tfcp = qla2x00_find_fcport_by_wwpn(vha, fcport->port_name, 1);
if (tfcp) {
tfcp->d_id = fcport->d_id;
tfcp->port_type = fcport->port_type;
tfcp->supported_classes = fcport->supported_classes;
tfcp->flags |= fcport->flags;
del = fcport;
fcport = tfcp;
} else {
if (vha->hw->current_topology == ISP_CFG_F)
fcport->flags |= FCF_FABRIC_DEVICE;
list_add_tail(&fcport->list, &vha->vp_fcports);
if (!IS_SW_RESV_ADDR(fcport->d_id))
vha->fcport_count++;
fcport->login_gen++;
fcport->disc_state = DSC_LOGIN_COMPLETE;
fcport->login_succ = 1;
newfcport = 1;
}
fcport->deleted = 0;
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
switch (vha->host->active_mode) {
case MODE_INITIATOR:
case MODE_DUAL:
if (newfcport) {
if (!IS_IIDMA_CAPABLE(vha->hw) || !vha->hw->flags.gpsc_supported) {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post upd_fcport fcp_cnt %d\n",
__func__, __LINE__, fcport->port_name, vha->fcport_count);
qla24xx_post_upd_fcport_work(vha, fcport);
} else {
ql_dbg(ql_dbg_disc, vha, 0xffff,
"%s %d %8phC post gpsc fcp_cnt %d\n",
__func__, __LINE__, fcport->port_name, vha->fcport_count);
qla24xx_post_gpsc_work(vha, fcport);
}
}
break;
case MODE_TARGET:
default:
break;
}
if (del)
qla2x00_free_fcport(del);
return fcport; return fcport;
} }
...@@ -5012,6 +5382,17 @@ static struct fc_port *qlt_make_local_sess(struct scsi_qla_host *vha, ...@@ -5012,6 +5382,17 @@ static struct fc_port *qlt_make_local_sess(struct scsi_qla_host *vha,
int rc, global_resets; int rc, global_resets;
uint16_t loop_id = 0; uint16_t loop_id = 0;
if ((s_id[0] == 0xFF) && (s_id[1] == 0xFC)) {
/*
* This is Domain Controller, so it should be
* OK to drop SCSI commands from it.
*/
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042,
"Unable to find initiator with S_ID %x:%x:%x",
s_id[0], s_id[1], s_id[2]);
return NULL;
}
mutex_lock(&vha->vha_tgt.tgt_mutex); mutex_lock(&vha->vha_tgt.tgt_mutex);
retry: retry:
...@@ -5022,21 +5403,11 @@ static struct fc_port *qlt_make_local_sess(struct scsi_qla_host *vha, ...@@ -5022,21 +5403,11 @@ static struct fc_port *qlt_make_local_sess(struct scsi_qla_host *vha,
if (rc != 0) { if (rc != 0) {
mutex_unlock(&vha->vha_tgt.tgt_mutex); mutex_unlock(&vha->vha_tgt.tgt_mutex);
if ((s_id[0] == 0xFF) && ql_log(ql_log_info, vha, 0xf071,
(s_id[1] == 0xFC)) { "qla_target(%d): Unable to find "
/* "initiator with S_ID %x:%x:%x",
* This is Domain Controller, so it should be vha->vp_idx, s_id[0], s_id[1],
* OK to drop SCSI commands from it. s_id[2]);
*/
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042,
"Unable to find initiator with S_ID %x:%x:%x",
s_id[0], s_id[1], s_id[2]);
} else
ql_log(ql_log_info, vha, 0xf071,
"qla_target(%d): Unable to find "
"initiator with S_ID %x:%x:%x",
vha->vp_idx, s_id[0], s_id[1],
s_id[2]);
if (rc == -ENOENT) { if (rc == -ENOENT) {
qlt_port_logo_t logo; qlt_port_logo_t logo;
...@@ -5104,12 +5475,18 @@ static void qlt_abort_work(struct qla_tgt *tgt, ...@@ -5104,12 +5475,18 @@ static void qlt_abort_work(struct qla_tgt *tgt,
if (!sess) if (!sess)
goto out_term2; goto out_term2;
} else { } else {
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { if (sess->deleted) {
sess = NULL; sess = NULL;
goto out_term2; goto out_term2;
} }
kref_get(&sess->sess_kref); if (!kref_get_unless_zero(&sess->sess_kref)) {
ql_dbg(ql_dbg_tgt_tmr, vha, 0xffff,
"%s: kref_get fail %8phC \n",
__func__, sess->port_name);
sess = NULL;
goto out_term2;
}
} }
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
...@@ -5133,7 +5510,8 @@ static void qlt_abort_work(struct qla_tgt *tgt, ...@@ -5133,7 +5510,8 @@ static void qlt_abort_work(struct qla_tgt *tgt,
qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false); qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
ha->tgt.tgt_ops->put_sess(sess); if (sess)
ha->tgt.tgt_ops->put_sess(sess);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2);
} }
...@@ -5168,12 +5546,18 @@ static void qlt_tmr_work(struct qla_tgt *tgt, ...@@ -5168,12 +5546,18 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
if (!sess) if (!sess)
goto out_term; goto out_term;
} else { } else {
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { if (sess->deleted) {
sess = NULL; sess = NULL;
goto out_term; goto out_term;
} }
kref_get(&sess->sess_kref); if (!kref_get_unless_zero(&sess->sess_kref)) {
ql_dbg(ql_dbg_tgt_tmr, vha, 0xffff,
"%s: kref_get fail %8phC\n",
__func__, sess->port_name);
sess = NULL;
goto out_term;
}
} }
iocb = a; iocb = a;
...@@ -5191,8 +5575,7 @@ static void qlt_tmr_work(struct qla_tgt *tgt, ...@@ -5191,8 +5575,7 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
out_term: out_term:
qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1, 0); qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1, 0);
if (sess) ha->tgt.tgt_ops->put_sess(sess);
ha->tgt.tgt_ops->put_sess(sess);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
} }
...@@ -5270,8 +5653,6 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) ...@@ -5270,8 +5653,6 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha)
tgt->vha = base_vha; tgt->vha = base_vha;
init_waitqueue_head(&tgt->waitQ); init_waitqueue_head(&tgt->waitQ);
INIT_LIST_HEAD(&tgt->del_sess_list); INIT_LIST_HEAD(&tgt->del_sess_list);
INIT_DELAYED_WORK(&tgt->sess_del_work,
(void (*)(struct work_struct *))qlt_del_sess_work_fn);
spin_lock_init(&tgt->sess_work_lock); spin_lock_init(&tgt->sess_work_lock);
INIT_WORK(&tgt->sess_work, qlt_sess_work_fn); INIT_WORK(&tgt->sess_work, qlt_sess_work_fn);
INIT_LIST_HEAD(&tgt->sess_works_list); INIT_LIST_HEAD(&tgt->sess_works_list);
...@@ -5472,6 +5853,7 @@ static void qlt_clear_mode(struct scsi_qla_host *vha) ...@@ -5472,6 +5853,7 @@ static void qlt_clear_mode(struct scsi_qla_host *vha)
break; break;
case QLA2XXX_INI_MODE_ENABLED: case QLA2XXX_INI_MODE_ENABLED:
vha->host->active_mode &= ~MODE_TARGET; vha->host->active_mode &= ~MODE_TARGET;
vha->host->active_mode |= MODE_INITIATOR;
break; break;
default: default:
break; break;
...@@ -5594,13 +5976,12 @@ qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req) ...@@ -5594,13 +5976,12 @@ qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req)
* FC-4 Feature bit 0 indicates target functionality to the name server. * FC-4 Feature bit 0 indicates target functionality to the name server.
*/ */
if (qla_tgt_mode_enabled(vha)) { if (qla_tgt_mode_enabled(vha)) {
if (qla_ini_mode_enabled(vha)) ct_req->req.rff_id.fc4_feature = BIT_0;
ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1;
else
ct_req->req.rff_id.fc4_feature = BIT_0;
} else if (qla_ini_mode_enabled(vha)) { } else if (qla_ini_mode_enabled(vha)) {
ct_req->req.rff_id.fc4_feature = BIT_1; ct_req->req.rff_id.fc4_feature = BIT_1;
} } else if (qla_dual_mode_enabled(vha))
ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1;
} }
/* /*
...@@ -5732,7 +6113,7 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) ...@@ -5732,7 +6113,7 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv)
nv->firmware_options_1 |= cpu_to_le32(BIT_4); nv->firmware_options_1 |= cpu_to_le32(BIT_4);
/* Disable ini mode, if requested */ /* Disable ini mode, if requested */
if (!qla_ini_mode_enabled(vha)) if (qla_tgt_mode_enabled(vha))
nv->firmware_options_1 |= cpu_to_le32(BIT_5); nv->firmware_options_1 |= cpu_to_le32(BIT_5);
/* Disable Full Login after LIP */ /* Disable Full Login after LIP */
...@@ -5834,7 +6215,7 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) ...@@ -5834,7 +6215,7 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv)
nv->firmware_options_1 |= cpu_to_le32(BIT_4); nv->firmware_options_1 |= cpu_to_le32(BIT_4);
/* Disable ini mode, if requested */ /* Disable ini mode, if requested */
if (!qla_ini_mode_enabled(vha)) if (qla_tgt_mode_enabled(vha))
nv->firmware_options_1 |= cpu_to_le32(BIT_5); nv->firmware_options_1 |= cpu_to_le32(BIT_5);
/* Disable Full Login after LIP */ /* Disable Full Login after LIP */
nv->firmware_options_1 &= cpu_to_le32(~BIT_13); nv->firmware_options_1 &= cpu_to_le32(~BIT_13);
...@@ -5940,8 +6321,9 @@ qlt_modify_vp_config(struct scsi_qla_host *vha, ...@@ -5940,8 +6321,9 @@ qlt_modify_vp_config(struct scsi_qla_host *vha,
{ {
if (qla_tgt_mode_enabled(vha)) if (qla_tgt_mode_enabled(vha))
vpmod->options_idx1 &= ~BIT_5; vpmod->options_idx1 &= ~BIT_5;
/* Disable ini mode, if requested */
if (!qla_ini_mode_enabled(vha)) /* Disable ini mode, if requested. */
if (qla_tgt_mode_enabled(vha))
vpmod->options_idx1 &= ~BIT_4; vpmod->options_idx1 &= ~BIT_4;
} }
......
...@@ -799,7 +799,6 @@ struct qla_tgt { ...@@ -799,7 +799,6 @@ struct qla_tgt {
/* Protected by hardware_lock */ /* Protected by hardware_lock */
struct list_head del_sess_list; struct list_head del_sess_list;
struct delayed_work sess_del_work;
spinlock_t sess_work_lock; spinlock_t sess_work_lock;
struct list_head sess_works_list; struct list_head sess_works_list;
...@@ -823,13 +822,6 @@ struct qla_tgt_sess_op { ...@@ -823,13 +822,6 @@ struct qla_tgt_sess_op {
bool aborted; bool aborted;
}; };
enum qla_sess_deletion {
QLA_SESS_DELETION_NONE = 0,
QLA_SESS_DELETION_PENDING = 1, /* hopefully we can get rid of
* this one */
QLA_SESS_DELETION_IN_PROGRESS = 2,
};
enum trace_flags { enum trace_flags {
TRC_NEW_CMD = BIT_0, TRC_NEW_CMD = BIT_0,
TRC_DO_WORK = BIT_1, TRC_DO_WORK = BIT_1,
...@@ -987,12 +979,17 @@ extern int ql2x_ini_mode; ...@@ -987,12 +979,17 @@ extern int ql2x_ini_mode;
static inline bool qla_tgt_mode_enabled(struct scsi_qla_host *ha) static inline bool qla_tgt_mode_enabled(struct scsi_qla_host *ha)
{ {
return ha->host->active_mode & MODE_TARGET; return ha->host->active_mode == MODE_TARGET;
} }
static inline bool qla_ini_mode_enabled(struct scsi_qla_host *ha) static inline bool qla_ini_mode_enabled(struct scsi_qla_host *ha)
{ {
return ha->host->active_mode & MODE_INITIATOR; return ha->host->active_mode == MODE_INITIATOR;
}
static inline bool qla_dual_mode_enabled(struct scsi_qla_host *ha)
{
return (ha->host->active_mode == MODE_DUAL);
} }
static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册