diff options
author | Quinn Tran <quinn.tran@cavium.com> | 2017-12-28 23:33:26 +0300 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2018-01-04 07:41:06 +0300 |
commit | a4239945b8ad112fb914d0605c8f6c5fd3330f61 (patch) | |
tree | b151d91a7e0a21d0d07689a7e341244acbbceee4 /drivers/scsi/qla2xxx | |
parent | 1429f0446a5b119bd80c1235ea4490c89b6c2f50 (diff) | |
download | linux-a4239945b8ad112fb914d0605c8f6c5fd3330f61.tar.xz |
scsi: qla2xxx: Add switch command to simplify fabric discovery
- add "async" gpn_ft, gnn_ft, gfpn_id, gnn_id switch commands.
- For 8G and newer adapters, use async commands when it comes to
fabric scan to reduce bottle neck.
Signed-off-by: Quinn Tran <quinn.tran@cavium.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_attr.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 72 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 15 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gs.c | 711 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_init.c | 308 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mbx.c | 5 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 47 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_target.c | 62 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_target.h | 2 |
9 files changed, 1103 insertions, 121 deletions
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 9ce28c4f9812..b360df9936ff 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -2170,6 +2170,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) dma_free_coherent(&ha->pdev->dev, vha->gnl.size, vha->gnl.l, vha->gnl.ldma); + vfree(vha->scan.l); + if (vha->qpair && vha->qpair->vp_idx == vha->vp_idx) { if (qla2xxx_delete_qpair(vha, vha->qpair) != QLA_SUCCESS) ql_log(ql_log_warn, vha, 0x7087, diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index bf50b4fbe648..240767c862e6 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2270,11 +2270,13 @@ struct ct_sns_desc { enum discovery_state { DSC_DELETED, + DSC_GNN_ID, DSC_GID_PN, DSC_GNL, DSC_LOGIN_PEND, DSC_LOGIN_FAILED, DSC_GPDB, + DSC_GFPN_ID, DSC_GPSC, DSC_UPD_FCPORT, DSC_LOGIN_COMPLETE, @@ -2304,8 +2306,9 @@ enum fcport_mgt_event { FCME_GPDB_DONE, FCME_GPNID_DONE, FCME_GFFID_DONE, - FCME_DELETE_DONE, FCME_ADISC_DONE, + FCME_GNNID_DONE, + FCME_GFPNID_DONE, }; enum rscn_addr_format { @@ -2338,6 +2341,7 @@ typedef struct fc_port { unsigned int login_pause:1; unsigned int login_succ:1; unsigned int query:1; + unsigned int id_changed:1; struct work_struct nvme_del_work; struct completion nvme_del_done; @@ -2485,6 +2489,11 @@ static const char * const port_state_str[] = { #define GA_NXT_REQ_SIZE (16 + 4) #define GA_NXT_RSP_SIZE (16 + 620) +#define GPN_FT_CMD 0x172 +#define GPN_FT_REQ_SIZE (16 + 4) +#define GNN_FT_CMD 0x173 +#define GNN_FT_REQ_SIZE (16 + 4) + #define GID_PT_CMD 0x1A1 #define GID_PT_REQ_SIZE (16 + 4) @@ -2740,6 +2749,13 @@ struct ct_sns_req { } port_id; struct { + uint8_t reserved; + uint8_t domain; + uint8_t area; + uint8_t port_type; + } gpn_ft; + + struct { uint8_t port_type; uint8_t domain; uint8_t area; @@ -2852,6 +2868,27 @@ struct ct_sns_gid_pt_data { uint8_t port_id[3]; }; +/* It's the same for both GPN_FT and GNN_FT */ +struct ct_sns_gpnft_rsp { + struct { + struct ct_cmd_hdr header; + uint16_t response; + uint16_t residual; + uint8_t fragment_id; + uint8_t reason_code; + uint8_t explanation_code; + uint8_t vendor_unique; + }; + /* Assume the largest number of targets for the union */ + struct ct_sns_gpn_ft_data { + u8 control_byte; + u8 port_id[3]; + u32 reserved; + u8 port_name[8]; + } entries[1]; +}; + +/* CT command response */ struct ct_sns_rsp { struct ct_rsp_hdr header; @@ -2927,6 +2964,24 @@ struct ct_sns_pkt { } p; }; +struct ct_sns_gpnft_pkt { + union { + struct ct_sns_req req; + struct ct_sns_gpnft_rsp rsp; + } p; +}; + +struct fab_scan_rp { + port_id_t id; + u8 port_name[8]; + u8 node_name[8]; +}; + +struct fab_scan { + struct fab_scan_rp *l; + u32 size; +}; + /* * SNS command structures -- for 2200 compatibility. */ @@ -3143,6 +3198,11 @@ enum qla_work_type { QLA_EVT_RELOGIN, QLA_EVT_ASYNC_PRLO, QLA_EVT_ASYNC_PRLO_DONE, + QLA_EVT_GPNFT, + QLA_EVT_GPNFT_DONE, + QLA_EVT_GNNFT_DONE, + QLA_EVT_GNNID, + QLA_EVT_GFPNID, }; @@ -3184,7 +3244,9 @@ struct qla_work_evt { struct { port_id_t id; u8 port_name[8]; + u8 node_name[8]; void *pla; + u8 fc4_type; } new_sess; struct { /*Get PDB, Get Speed, update fcport, gnl, gidpn */ fc_port_t *fcport; @@ -3195,6 +3257,9 @@ struct qla_work_evt { u8 iocb[IOCB_SIZE]; int type; } nack; + struct { + u8 fc4_type; + } gpnft; } u; }; @@ -3729,6 +3794,8 @@ struct qla_hw_data { (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_EXLOGIN_OFFLD_CAPABLE(ha) \ (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha)) +#define USE_ASYNC_SCAN(ha) (IS_QLA25XX(ha) || IS_QLA81XX(ha) ||\ + IS_QLA83XX(ha) || IS_QLA27XX(ha)) /* HBA serial number */ uint8_t serial0; @@ -3811,7 +3878,7 @@ struct qla_hw_data { int exchoffld_size; int exchoffld_count; - void *swl; + void *swl; /* These are used by mailbox operations. */ uint16_t mailbox_out[MAILBOX_REGISTER_COUNT]; @@ -4271,6 +4338,7 @@ typedef struct scsi_qla_host { uint8_t n2n_port_name[WWN_SIZE]; uint16_t n2n_id; struct list_head gpnid_list; + struct fab_scan scan; } scsi_qla_host_t; struct qla27xx_image_status { diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 9d7b66abfc10..4e504e5e7586 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -105,8 +105,8 @@ int qla24xx_async_gpdb(struct scsi_qla_host *, fc_port_t *, u8); int qla24xx_async_prli(struct scsi_qla_host *, fc_port_t *); 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_post_newsess_work(struct scsi_qla_host *, port_id_t *, u8 *, u8*, + void *, u8); int qla24xx_fcport_handle_login(struct scsi_qla_host *, fc_port_t *); int qla24xx_detect_sfp(scsi_qla_host_t *vha); int qla24xx_post_gpdb_work(struct scsi_qla_host *, fc_port_t *, u8); @@ -655,9 +655,20 @@ 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 *); +void qla24xx_handle_gpsc_event(scsi_qla_host_t *, struct event_arg *); int qla2x00_mgmt_svr_login(scsi_qla_host_t *); void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea); int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport); +int qla24xx_async_gpnft(scsi_qla_host_t *, u8); +void qla24xx_async_gpnft_done(scsi_qla_host_t *, srb_t *); +void qla24xx_async_gnnft_done(scsi_qla_host_t *, srb_t *); +int qla24xx_async_gnnid(scsi_qla_host_t *, fc_port_t *); +void qla24xx_handle_gnnid_event(scsi_qla_host_t *, struct event_arg *); +int qla24xx_post_gnnid_work(struct scsi_qla_host *, fc_port_t *); +int qla24xx_post_gfpnid_work(struct scsi_qla_host *, fc_port_t *); +int qla24xx_async_gfpnid(scsi_qla_host_t *, fc_port_t *); +void qla24xx_handle_gfpnid_event(scsi_qla_host_t *, struct event_arg *); + /* * Global Function Prototypes in qla_attr.c source file. */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index bb96219ce525..2132c7ad8044 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -2796,6 +2796,9 @@ void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea) "%s %8phC login state %d\n", __func__, fcport->port_name, fcport->fw_login_state); + if (fcport->disc_state == DSC_DELETE_PEND) + return; + if (ea->sp->gen2 != fcport->login_gen) { /* PLOGI/PRLI/LOGO came in while cmd was out.*/ ql_dbg(ql_dbg_disc, vha, 0x201e, @@ -2814,7 +2817,21 @@ void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea) /* cable plugged into the same place */ switch (vha->host->active_mode) { case MODE_TARGET: - /* NOOP. let the other guy login to us.*/ + if (fcport->fw_login_state == + DSC_LS_PRLI_COMP) { + u16 data[2]; + /* + * Late RSCN was delivered. + * Remote port already login'ed. + */ + ql_dbg(ql_dbg_disc, vha, 0x201f, + "%s %d %8phC post adisc\n", + __func__, __LINE__, + fcport->port_name); + data[0] = data[1] = 0; + qla2x00_post_async_adisc_work( + vha, fcport, data); + } break; case MODE_INITIATOR: case MODE_DUAL: @@ -2840,6 +2857,7 @@ void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea) } } else { /* fcport->d_id.b24 != ea->id.b24 */ fcport->d_id.b24 = ea->id.b24; + fcport->id_changed = 1; if (fcport->deleted != QLA_SESS_DELETED) { ql_dbg(ql_dbg_disc, vha, 0x2021, "%s %d %8phC post del sess\n", @@ -3009,6 +3027,38 @@ int qla24xx_post_gpsc_work(struct scsi_qla_host *vha, fc_port_t *fcport) return qla2x00_post_work(vha, e); } +void qla24xx_handle_gpsc_event(scsi_qla_host_t *vha, struct event_arg *ea) +{ + struct fc_port *fcport = ea->fcport; + + ql_dbg(ql_dbg_disc, vha, 0x20d8, + "%s %8phC DS %d LS %d rscn %d|%d login %d|%d lid %d\n", + __func__, fcport->port_name, fcport->disc_state, + fcport->fw_login_state, fcport->last_rscn_gen, fcport->rscn_gen, + fcport->last_login_gen, fcport->login_gen, + fcport->loop_id); + + if (fcport->disc_state == DSC_DELETE_PEND) + return; + + if (ea->sp->gen2 != fcport->login_gen) { + /* target side must have changed it. */ + ql_dbg(ql_dbg_disc, vha, 0x20d3, + "%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, 0x20d4, "%s %d %8phC post gidpn\n", + __func__, __LINE__, fcport->port_name); + qla24xx_post_gidpn_work(vha, fcport); + return; + } + + qla24xx_post_upd_fcport_work(vha, ea->fcport); +} + static void qla24xx_async_gpsc_sp_done(void *s, int res) { struct srb *sp = s; @@ -3075,6 +3125,7 @@ done: ea.event = FCME_GPSC_DONE; ea.rc = res; ea.fcport = fcport; + ea.sp = sp; qla2x00_fcport_event_handler(vha, &ea); sp->free(sp); @@ -3305,7 +3356,7 @@ void qla24xx_handle_gpnid_event(scsi_qla_host_t *vha, struct event_arg *ea) "%s %d %8phC post new sess\n", __func__, __LINE__, ea->port_name); qla24xx_post_newsess_work(vha, &ea->id, - ea->port_name, NULL); + ea->port_name, NULL, NULL, FC4_TYPE_UNKNOWN); } } } @@ -3595,3 +3646,659 @@ done_free_sp: fcport->flags &= ~FCF_ASYNC_SENT; return rval; } + +/* GPN_FT + GNN_FT*/ +static int qla2x00_is_a_vp(scsi_qla_host_t *vha, u64 wwn) +{ + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *vp; + unsigned long flags; + u64 twwn; + int rc = 0; + + if (!ha->num_vhosts) + return 0; + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + twwn = wwn_to_u64(vp->port_name); + if (wwn == twwn) { + rc = 1; + break; + } + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + + return rc; +} + +void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) +{ + fc_port_t *fcport; + u32 i, rc; + bool found; + u8 fc4type = sp->gen2; + struct fab_scan_rp *rp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s enter\n", __func__); + + if (sp->gen1 != vha->hw->base_qpair->chip_reset) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s scan stop due to chip reset %x/%x\n", + sp->name, sp->gen1, vha->hw->base_qpair->chip_reset); + goto out; + } + + rc = sp->rc; + if (rc) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "GPNFT failed. FC4type %x. Rescanning.\n", + fc4type); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + goto out; + } + + list_for_each_entry(fcport, &vha->vp_fcports, list) + fcport->scan_state = QLA_FCPORT_SCAN; + + for (i = 0; i < vha->hw->max_fibre_devices; i++) { + u64 wwn; + + rp = &vha->scan.l[i]; + found = false; + + wwn = wwn_to_u64(rp->port_name); + if (wwn == 0) + continue; + + if (!memcmp(rp->port_name, vha->port_name, WWN_SIZE)) + continue; + + /* Bypass reserved domain fields. */ + if ((rp->id.b.domain & 0xf0) == 0xf0) + continue; + + /* Bypass virtual ports of the same host. */ + if (qla2x00_is_a_vp(vha, wwn)) + continue; + + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (memcmp(rp->port_name, fcport->port_name, WWN_SIZE)) + continue; + fcport->scan_state = QLA_FCPORT_FOUND; + fcport->d_id.b24 = rp->id.b24; + found = true; + /* + * If device was not a fabric device before. + */ + if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) { + qla2x00_clear_loop_id(fcport); + fcport->flags |= FCF_FABRIC_DEVICE; + } + break; + } + + if (!found) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post new sess\n", + __func__, __LINE__, rp->port_name); + qla24xx_post_newsess_work(vha, &rp->id, rp->port_name, + rp->node_name, NULL, fc4type); + } + } + + /* + * Logout all previous fabric dev marked lost, except FCP2 devices. + */ + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if ((fcport->flags & FCF_FABRIC_DEVICE) == 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, 0x20f0, + "%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); + } + +out: + /* re-use gpnid_done to free resource */ + qla24xx_async_gpnid_done(vha, sp); +} + +static void qla2x00_async_gpnft_gnnft_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + struct qla_work_evt *e; + struct ct_sns_req *ct_req = + (struct ct_sns_req *)sp->u.iocb_cmd.u.ctarg.req; + struct ct_sns_gpnft_rsp *ct_rsp = + (struct ct_sns_gpnft_rsp *)sp->u.iocb_cmd.u.ctarg.rsp; + struct ct_sns_gpn_ft_data *d = &ct_rsp->entries[0]; + struct fab_scan_rp *rp; + int i, j, k; + u16 cmd = be16_to_cpu(ct_req->command); + + /* gen2 field is holding the fc4type */ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x FC4Type %x\n", + sp->name, res, sp->gen2); + + if (!res) { + port_id_t id; + u64 wwn; + + j = 0; + for (i = 0; i < vha->hw->max_fibre_devices; i++) { + d = &ct_rsp->entries[i]; + + id.b.rsvd_1 = 0; + id.b.domain = d->port_id[0]; + id.b.area = d->port_id[1]; + id.b.al_pa = d->port_id[2]; + wwn = wwn_to_u64(d->port_name); + + if (id.b24 == 0 || wwn == 0) + continue; + + if (cmd == GPN_FT_CMD) { + rp = &vha->scan.l[j]; + rp->id = id; + memcpy(rp->port_name, d->port_name, 8); + j++; + } else {/* GNN_FT_CMD */ + for (k = 0; k < vha->hw->max_fibre_devices; + k++) { + rp = &vha->scan.l[k]; + if (id.b24 == rp->id.b24) { + memcpy(rp->node_name, + d->port_name, 8); + break; + } + } + } + } + } + + if (cmd == GPN_FT_CMD) + e = qla2x00_alloc_work(vha, QLA_EVT_GPNFT_DONE); + else + e = qla2x00_alloc_work(vha, QLA_EVT_GNNFT_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; + } + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s unable to alloc work element\n", + sp->name); + sp->free(sp); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + return; + } + + sp->rc = res; + e->u.iosb.sp = sp; + + qla2x00_post_work(vha, e); +} + +/* + * Get WWNN list for fc4_type + * + * It is assumed the same SRB is re-used from GPNFT to avoid + * mem free & re-alloc + */ +static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp, + u8 fc4_type) +{ + int rval = QLA_FUNCTION_FAILED; + struct ct_sns_req *ct_req; + struct ct_sns_pkt *ct_sns; + + if (!vha->flags.online) + goto done_free_sp; + + if (!sp->u.iocb_cmd.u.ctarg.req || !sp->u.iocb_cmd.u.ctarg.rsp) { + ql_log(ql_log_warn, vha, 0xffff, + "%s: req %p rsp %p are not setup\n", + __func__, sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.rsp); + WARN_ON(1); + goto done_free_sp; + } + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gnnft"; + sp->gen1 = vha->hw->base_qpair->chip_reset; + sp->gen2 = fc4_type; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + memset(sp->u.iocb_cmd.u.ctarg.rsp, 0, sp->u.iocb_cmd.u.ctarg.rsp_size); + memset(sp->u.iocb_cmd.u.ctarg.req, 0, sp->u.iocb_cmd.u.ctarg.req_size); + + ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req; + /* CT_IU preamble */ + ct_req = qla2x00_prep_ct_req(ct_sns, GNN_FT_CMD, + sp->u.iocb_cmd.u.ctarg.rsp_size); + + /* GPN_FT req */ + ct_req->req.gpn_ft.port_type = fc4_type; + + sp->u.iocb_cmd.u.ctarg.req_size = GNN_FT_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS; + + sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_gpnft_gnnft_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 FC4Type %x.\n", sp->name, + sp->handle, ct_req->req.gpn_ft.port_type); + 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(sp); + + return rval; +} /* GNNFT */ + +void qla24xx_async_gpnft_done(scsi_qla_host_t *vha, srb_t *sp) +{ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s enter\n", __func__); + del_timer(&sp->u.iocb_cmd.timer); + qla24xx_async_gnnft(vha, sp, sp->gen2); +} + +/* Get WWPN list for certain fc4_type */ +int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type) +{ + int rval = QLA_FUNCTION_FAILED; + struct ct_sns_req *ct_req; + srb_t *sp; + struct ct_sns_pkt *ct_sns; + u32 rspsz; + + if (!vha->flags.online) + return rval; + + sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL); + if (!sp) + return rval; + + + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gpnft"; + sp->gen1 = vha->hw->base_qpair->chip_reset; + sp->gen2 = fc4_type; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + sp->u.iocb_cmd.u.ctarg.req = dma_zalloc_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; + } + + rspsz = sizeof(struct ct_sns_gpnft_rsp) + + ((vha->hw->max_fibre_devices - 1) * + sizeof(struct ct_sns_gpn_ft_data)); + + sp->u.iocb_cmd.u.ctarg.rsp = dma_zalloc_coherent(&vha->hw->pdev->dev, + rspsz, &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; + } + + memset(vha->scan.l, 0, vha->scan.size); + + 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_FT_CMD, rspsz); + + /* GPN_FT req */ + ct_req->req.gpn_ft.port_type = fc4_type; + + sp->u.iocb_cmd.u.ctarg.req_size = GPN_FT_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.rsp_size = rspsz; + sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS; + + sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_gpnft_gnnft_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 FC4Type %x.\n", sp->name, + sp->handle, ct_req->req.gpn_ft.port_type); + 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(sp); + + return rval; +} + +/* GNN_ID */ +void qla24xx_handle_gnnid_event(scsi_qla_host_t *vha, struct event_arg *ea) +{ + qla24xx_post_gnl_work(vha, ea->fcport); +} + +static void qla2x00_async_gnnid_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + fc_port_t *fcport = sp->fcport; + u8 *node_name = fcport->ct_desc.ct_sns->p.rsp.rsp.gnn_id.node_name; + struct event_arg ea; + u64 wwnn; + + fcport->flags &= ~FCF_ASYNC_SENT; + wwnn = wwn_to_u64(node_name); + if (wwnn) + memcpy(fcport->node_name, node_name, WWN_SIZE); + + memset(&ea, 0, sizeof(ea)); + ea.fcport = fcport; + ea.sp = sp; + ea.rc = res; + ea.event = FCME_GNNID_DONE; + + ql_dbg(ql_dbg_disc, vha, 0x204f, + "Async done-%s res %x, WWPN %8phC %8phC\n", + sp->name, res, fcport->port_name, fcport->node_name); + + qla2x00_fcport_event_handler(vha, &ea); + + sp->free(sp); +} + +int qla24xx_async_gnnid(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_GNN_ID; + sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); + if (!sp) + goto done; + + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gnnid"; + 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, GNN_ID_CMD, + GNN_ID_RSP_SIZE); + + /* GNN_ID req */ + ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain; + ct_req->req.port_id.port_id[1] = fcport->d_id.b.area; + ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa; + + + /* 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 = GNN_ID_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.rsp_size = GNN_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_gnnid_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 %06x.\n", + sp->name, fcport->port_name, + sp->handle, fcport->loop_id, fcport->d_id.b24); + return rval; + +done_free_sp: + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + return rval; +} + +int qla24xx_post_gnnid_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + int ls; + + ls = atomic_read(&vha->loop_state); + if (((ls != LOOP_READY) && (ls != LOOP_UP)) || + test_bit(UNLOADING, &vha->dpc_flags)) + return 0; + + e = qla2x00_alloc_work(vha, QLA_EVT_GNNID); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} + +/* GPFN_ID */ +void qla24xx_handle_gfpnid_event(scsi_qla_host_t *vha, struct event_arg *ea) +{ + fc_port_t *fcport = ea->fcport; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpsc fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, + vha->fcport_count); + + if (fcport->disc_state == DSC_DELETE_PEND) + return; + + if (ea->sp->gen2 != fcport->login_gen) { + /* target side must have changed it. */ + ql_dbg(ql_dbg_disc, vha, 0x20d3, + "%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, 0x20d4, "%s %d %8phC post gidpn\n", + __func__, __LINE__, fcport->port_name); + qla24xx_post_gidpn_work(vha, fcport); + return; + } + + qla24xx_post_gpsc_work(vha, fcport); +} + +static void qla2x00_async_gfpnid_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + fc_port_t *fcport = sp->fcport; + u8 *fpn = fcport->ct_desc.ct_sns->p.rsp.rsp.gfpn_id.port_name; + struct event_arg ea; + u64 wwn; + + fcport->flags &= ~FCF_ASYNC_SENT; + wwn = wwn_to_u64(fpn); + if (wwn) + memcpy(fcport->fabric_port_name, fpn, WWN_SIZE); + + memset(&ea, 0, sizeof(ea)); + ea.fcport = fcport; + ea.sp = sp; + ea.rc = res; + ea.event = FCME_GFPNID_DONE; + + ql_dbg(ql_dbg_disc, vha, 0x204f, + "Async done-%s res %x, WWPN %8phC %8phC\n", + sp->name, res, fcport->port_name, fcport->fabric_port_name); + + qla2x00_fcport_event_handler(vha, &ea); + + sp->free(sp); +} + +int qla24xx_async_gfpnid(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_GFPN_ID; + sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); + if (!sp) + goto done; + + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gfpnid"; + 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, GFPN_ID_CMD, + GFPN_ID_RSP_SIZE); + + /* GFPN_ID req */ + ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain; + ct_req->req.port_id.port_id[1] = fcport->d_id.b.area; + ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa; + + + /* 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 = GFPN_ID_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.rsp_size = GFPN_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_gfpnid_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 %06x.\n", + sp->name, fcport->port_name, + sp->handle, fcport->loop_id, fcport->d_id.b24); + return rval; + +done_free_sp: + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + return rval; +} + +int qla24xx_post_gfpnid_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + int ls; + + ls = atomic_read(&vha->loop_state); + if (((ls != LOOP_READY) && (ls != LOOP_UP)) || + test_bit(UNLOADING, &vha->dpc_flags)) + return 0; + + e = qla2x00_alloc_work(vha, QLA_EVT_GFPNID); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index c671852131ef..f26acb7ce315 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -41,7 +41,7 @@ static void qla24xx_handle_plogi_done_event(struct scsi_qla_host *, struct event_arg *); static void qla24xx_handle_prli_done_event(struct scsi_qla_host *, struct event_arg *); -static void qla24xx_handle_gpdb_event(scsi_qla_host_t *, struct event_arg *); +static void __qla24xx_handle_gpdb_event(scsi_qla_host_t *, struct event_arg *); /* SRB Extensions ---------------------------------------------------------- */ @@ -188,8 +188,11 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, fcport->flags |= FCF_ASYNC_SENT; fcport->logout_completed = 0; + fcport->disc_state = DSC_LOGIN_PEND; sp->type = SRB_LOGIN_CMD; sp->name = "login"; + sp->gen1 = fcport->rscn_gen; + sp->gen2 = fcport->login_gen; qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); lio = &sp->u.iocb_cmd; @@ -336,7 +339,36 @@ done: static void qla24xx_handle_adisc_event(scsi_qla_host_t *vha, struct event_arg *ea) { - qla24xx_handle_gpdb_event(vha, ea); + if (ea->rc) { + ql_dbg(ql_dbg_disc, vha, 0x2066, + "%s %8phC: adisc fail: post delete\n", + __func__, ea->fcport->port_name); + qlt_schedule_sess_for_deletion(ea->fcport, 1); + return; + } + ql_dbg(ql_dbg_disc, vha, 0x20d2, + "%s %8phC DS %d LS %d\n", __func__, ea->fcport->port_name, + ea->fcport->disc_state, ea->fcport->fw_login_state); + + if (ea->fcport->disc_state == DSC_DELETE_PEND) + return; + + if (ea->sp->gen2 != ea->fcport->login_gen) { + /* target side must have changed it. */ + ql_dbg(ql_dbg_disc, vha, 0x20d3, + "%s %8phC generation changed rscn %d|%d login %d|%d\n", + __func__, ea->fcport->port_name, ea->fcport->last_rscn_gen, + ea->fcport->rscn_gen, ea->fcport->last_login_gen, + ea->fcport->login_gen); + return; + } else if (ea->sp->gen1 != ea->fcport->rscn_gen) { + ql_dbg(ql_dbg_disc, vha, 0x20d4, "%s %d %8phC post gidpn\n", + __func__, __LINE__, ea->fcport->port_name); + qla24xx_post_gidpn_work(vha, ea->fcport); + return; + } + + __qla24xx_handle_gpdb_event(vha, ea); } static void @@ -409,10 +441,14 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, u16 i, n, found = 0, loop_id; port_id_t id; u64 wwn; - u8 opt = 0, current_login_state; + u16 data[2]; + u8 current_login_state; fcport = ea->fcport; + if (fcport->disc_state == DSC_DELETE_PEND) + return; + if (ea->rc) { /* rval */ if (fcport->login_retry == 0) { fcport->login_retry = vha->hw->login_retry_count; @@ -506,8 +542,14 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, ql_dbg(ql_dbg_disc, vha, 0x20e4, "%s %d %8phC post gpdb\n", __func__, __LINE__, fcport->port_name); - opt = PDO_FORCE_ADISC; - qla24xx_post_gpdb_work(vha, fcport, opt); + + if ((e->prli_svc_param_word_3[0] & BIT_4) == 0) + fcport->port_type = FCT_INITIATOR; + else + fcport->port_type = FCT_TARGET; + + data[0] = data[1] = 0; + qla2x00_post_async_adisc_work(vha, fcport, data); break; case DSC_LS_PORT_UNAVAIL: default: @@ -572,6 +614,7 @@ qla24xx_async_gnl_sp_done(void *s, int res) struct get_name_list_extended *e; u64 wwn; struct list_head h; + bool found = false; ql_dbg(ql_dbg_disc, vha, 0x20e7, "Async done-%s res %x mb[1]=%x mb[2]=%x \n", @@ -621,6 +664,38 @@ qla24xx_async_gnl_sp_done(void *s, int res) qla2x00_fcport_event_handler(vha, &ea); } + /* create new fcport if fw has knowledge of new sessions */ + for (i = 0; i < n; i++) { + port_id_t id; + u64 wwnn; + + e = &vha->gnl.l[i]; + wwn = wwn_to_u64(e->port_name); + + found = false; + list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { + if (!memcmp((u8 *)&wwn, fcport->port_name, + WWN_SIZE)) { + found = true; + break; + } + } + + 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; + + if (!found && wwn && !IS_SW_RESV_ADDR(id)) { + ql_dbg(ql_dbg_disc, vha, 0x2065, + "%s %d %8phC post new sess\n", + __func__, __LINE__, (u8 *)&wwn); + wwnn = wwn_to_u64(e->node_name); + qla24xx_post_newsess_work(vha, &id, (u8 *)&wwn, + (u8 *)&wwnn, NULL, FC4_TYPE_UNKNOWN); + } + } + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); sp->free(sp); @@ -715,10 +790,8 @@ void qla24xx_async_gpdb_sp_done(void *s, int res) struct srb *sp = s; struct scsi_qla_host *vha = sp->vha; struct qla_hw_data *ha = vha->hw; - 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, 0x20db, @@ -727,19 +800,8 @@ void qla24xx_async_gpdb_sp_done(void *s, int res) 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; - - rval = __qla24xx_parse_gpdb(vha, fcport, pd); - -gpd_error_out: memset(&ea, 0, sizeof(ea)); ea.event = FCME_GPDB_DONE; - ea.rc = rval; ea.fcport = fcport; ea.sp = sp; @@ -934,41 +996,10 @@ done: } static -void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) +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, 0x20d2, - "%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, 0x20d3, - "%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); - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - return; - } else if (ea->sp->gen1 != fcport->rscn_gen) { - ql_dbg(ql_dbg_disc, vha, 0x20d4, "%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, 0x20d5, "%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; @@ -982,32 +1013,81 @@ void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) !vha->hw->flags.gpsc_supported) { ql_dbg(ql_dbg_disc, vha, 0x20d6, "%s %d %8phC post upd_fcport fcp_cnt %d\n", - __func__, __LINE__, fcport->port_name, + __func__, __LINE__, ea->fcport->port_name, vha->fcport_count); - qla24xx_post_upd_fcport_work(vha, fcport); + qla24xx_post_upd_fcport_work(vha, ea->fcport); } else { - ql_dbg(ql_dbg_disc, vha, 0x20d7, - "%s %d %8phC post gpsc fcp_cnt %d\n", - __func__, __LINE__, fcport->port_name, - vha->fcport_count); - - qla24xx_post_gpsc_work(vha, fcport); + if (ea->fcport->id_changed) { + ea->fcport->id_changed = 0; + ql_dbg(ql_dbg_disc, vha, 0x20d7, + "%s %d %8phC post gfpnid fcp_cnt %d\n", + __func__, __LINE__, ea->fcport->port_name, + vha->fcport_count); + qla24xx_post_gfpnid_work(vha, ea->fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0x20d7, + "%s %d %8phC post gpsc fcp_cnt %d\n", + __func__, __LINE__, ea->fcport->port_name, + vha->fcport_count); + qla24xx_post_gpsc_work(vha, ea->fcport); + } } } else if (ea->fcport->login_succ) { /* * We have an existing session. A late RSCN delivery * must have triggered the session to be re-validate. - * session is still valid. + * Session is still valid. */ ql_dbg(ql_dbg_disc, vha, 0x20d6, "%s %d %8phC session revalidate success\n", - __func__, __LINE__, fcport->port_name); - fcport->disc_state = DSC_LOGIN_COMPLETE; + __func__, __LINE__, ea->fcport->port_name); + ea->fcport->disc_state = DSC_LOGIN_COMPLETE; } spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); -} /* gpdb event */ +} + +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; + struct port_database_24xx *pd; + struct srb *sp = ea->sp; + + pd = (struct port_database_24xx *)sp->u.iocb_cmd.u.mbx.in; + + fcport->flags &= ~FCF_ASYNC_SENT; + + ql_dbg(ql_dbg_disc, vha, 0x20d2, + "%s %8phC DS %d LS %d rval %d\n", __func__, fcport->port_name, + fcport->disc_state, pd->current_login_state, rval); + + if (fcport->disc_state == DSC_DELETE_PEND) + return; + switch (pd->current_login_state) { + case PDS_PRLI_COMPLETE: + __qla24xx_parse_gpdb(vha, fcport, pd); + break; + case PDS_PLOGI_PENDING: + case PDS_PLOGI_COMPLETE: + case PDS_PRLI_PENDING: + case PDS_PRLI2_PENDING: + ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC relogin needed\n", + __func__, __LINE__, fcport->port_name); + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + return; + case PDS_LOGO_PENDING: + case PDS_PORT_UNAVAILABLE: + default: + ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC post del sess\n", + __func__, __LINE__, fcport->port_name); + qlt_schedule_sess_for_deletion_lock(fcport); + return; + } + __qla24xx_handle_gpdb_event(vha, ea); +} /* gpdb event */ static void qla_chk_n2n_b4_login(struct scsi_qla_host *vha, fc_port_t *fcport) { @@ -1048,21 +1128,21 @@ static void qla_chk_n2n_b4_login(struct scsi_qla_host *vha, fc_port_t *fcport) int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) { u16 data[2]; - if (fcport->login_retry == 0) - return 0; - - if (fcport->scan_state != QLA_FCPORT_FOUND) - return 0; + u64 wwn; ql_dbg(ql_dbg_disc, vha, 0x20d8, - "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d|%d retry %d lid %d\n", + "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d|%d retry %d lid %d scan %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->loop_id, fcport->scan_state); - fcport->login_retry--; + if (fcport->login_retry == 0) + return 0; + + if (fcport->scan_state != QLA_FCPORT_FOUND) + return 0; if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || (fcport->fw_login_state == DSC_LS_PRLI_PEND)) @@ -1084,9 +1164,17 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) return 0; } + fcport->login_retry--; + switch (fcport->disc_state) { case DSC_DELETED: - if (fcport->loop_id == FC_NO_LOOP_ID) { + wwn = wwn_to_u64(fcport->node_name); + if (wwn == 0) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post GNNID\n", + __func__, __LINE__, fcport->port_name); + qla24xx_post_gnnid_work(vha, fcport); + } else if (fcport->loop_id == FC_NO_LOOP_ID) { ql_dbg(ql_dbg_disc, vha, 0x20bd, "%s %d %8phC post gnl\n", __func__, __LINE__, fcport->port_name); @@ -1157,7 +1245,7 @@ void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea) } int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, - u8 *port_name, void *pla) + u8 *port_name, u8 *node_name, void *pla, u8 fc4_type) { struct qla_work_evt *e; e = qla2x00_alloc_work(vha, QLA_EVT_NEW_SESS); @@ -1166,37 +1254,15 @@ int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, e->u.new_sess.id = *id; e->u.new_sess.pla = pla; + e->u.new_sess.fc4_type = fc4_type; memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE); + if (node_name) + memcpy(e->u.new_sess.node_name, node_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) { @@ -1261,6 +1327,7 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) case FCME_GIDPN_DONE: case FCME_GPSC_DONE: case FCME_GPNID_DONE: + case FCME_GNNID_DONE: if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) || test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags)) return; @@ -1337,7 +1404,7 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) qla24xx_handle_gnl_done_event(vha, ea); break; case FCME_GPSC_DONE: - qla24xx_post_upd_fcport_work(vha, ea->fcport); + qla24xx_handle_gpsc_event(vha, ea); break; case FCME_PLOGI_DONE: /* Initiator side sent LLIOCB */ qla24xx_handle_plogi_done_event(vha, ea); @@ -1354,12 +1421,15 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) case FCME_GFFID_DONE: qla24xx_handle_gffid_event(vha, ea); break; - case FCME_DELETE_DONE: - qla24xx_handle_delete_done_event(vha, ea); - break; case FCME_ADISC_DONE: qla24xx_handle_adisc_event(vha, ea); break; + case FCME_GNNID_DONE: + qla24xx_handle_gnnid_event(vha, ea); + break; + case FCME_GFPNID_DONE: + qla24xx_handle_gfpnid_event(vha, ea); + break; default: BUG_ON(1); break; @@ -1568,6 +1638,33 @@ qla24xx_handle_plogi_done_event(struct scsi_qla_host *vha, struct event_arg *ea) u16 lid; struct fc_port *conflict_fcport; unsigned long flags; + struct fc_port *fcport = ea->fcport; + + if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || + (fcport->fw_login_state == DSC_LS_PRLI_PEND)) { + ql_dbg(ql_dbg_disc, vha, 0x20ea, + "%s %d %8phC Remote is trying to login\n", + __func__, __LINE__, fcport->port_name); + return; + } + + if (fcport->disc_state == DSC_DELETE_PEND) + return; + + if (ea->sp->gen2 != fcport->login_gen) { + /* target side must have changed it. */ + ql_dbg(ql_dbg_disc, vha, 0x20d3, + "%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, 0x20d4, "%s %d %8phC post gidpn\n", + __func__, __LINE__, fcport->port_name); + qla24xx_post_gidpn_work(vha, fcport); + return; + } switch (ea->data[0]) { case MBS_COMMAND_COMPLETE: @@ -5124,7 +5221,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) * will be newer than discovery_gen. */ qlt_do_generation_tick(vha, &discovery_gen); - rval = qla2x00_find_all_fabric_devs(vha); + if (USE_ASYNC_SCAN(ha)) { + rval = QLA_SUCCESS; + rval = qla24xx_async_gpnft(vha, FC4_TYPE_FCP_SCSI); + if (rval) + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + } else { + rval = qla2x00_find_all_fabric_devs(vha); + } if (rval != QLA_SUCCESS) break; } while (0); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 4c2f85b67ad1..8455058cd724 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3904,7 +3904,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, id.b.area = rptid_entry->u.f2.remote_nport_id[1]; id.b.domain = rptid_entry->u.f2.remote_nport_id[2]; qla24xx_post_newsess_work(vha, &id, - rptid_entry->u.f2.port_name, NULL); + rptid_entry->u.f2.port_name, + rptid_entry->u.f2.node_name, + NULL, + FC4_TYPE_UNKNOWN); } } } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 4c3527f54b74..5d909f4ab6c2 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3639,6 +3639,8 @@ qla2x00_remove_one(struct pci_dev *pdev) dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma); + vfree(base_vha->scan.l); + if (IS_QLAFX00(ha)) qlafx00_driver_shutdown(base_vha, 20); @@ -4587,6 +4589,18 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, return NULL; } + /* todo: what about ext login? */ + vha->scan.size = ha->max_fibre_devices * sizeof(struct fab_scan_rp); + vha->scan.l = vmalloc(vha->scan.size); + if (!vha->scan.l) { + ql_log(ql_log_fatal, vha, 0xd04a, + "Alloc failed for scan database.\n"); + dma_free_coherent(&ha->pdev->dev, vha->gnl.size, + vha->gnl.l, vha->gnl.ldma); + scsi_remove_host(vha->host); + return NULL; + } + sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no); ql_dbg(ql_dbg_init, vha, 0x0041, "Allocated the host=%p hw=%p vha=%p dev_name=%s", @@ -4760,6 +4774,7 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) struct qlt_plogi_ack_t *pla = (struct qlt_plogi_ack_t *)e->u.new_sess.pla; uint8_t free_fcport = 0; + u64 wwn; ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC enter\n", @@ -4785,9 +4800,10 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) 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; + if (e->u.new_sess.fc4_type == FC4_TYPE_FCP_SCSI) + fcport->fc4_type = FC4_TYPE_FCP_SCSI; memcpy(fcport->port_name, e->u.new_sess.port_name, WWN_SIZE); @@ -4802,7 +4818,7 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) } spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); - /* search again to make sure one else got ahead */ + /* search again to make sure no one else got ahead */ tfcp = qla2x00_find_fcport_by_wwpn(vha, e->u.new_sess.port_name, 1); if (tfcp) { @@ -4829,6 +4845,10 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) if (N2N_TOPO(vha->hw)) fcport->flags &= ~FCF_FABRIC_DEVICE; + fcport->id_changed = 1; + fcport->scan_state = QLA_FCPORT_FOUND; + memcpy(fcport->node_name, e->u.new_sess.node_name, WWN_SIZE); + if (pla) { if (pla->iocb.u.isp24.status_subcode == ELS_PRLI) { u16 wd3_lo; @@ -4881,7 +4901,13 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) } } spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); - qla24xx_async_gnl(vha, fcport); + + wwn = wwn_to_u64(fcport->node_name); + + if (!wwn) + qla24xx_async_gnnid(vha, fcport); + else + qla24xx_async_gnl(vha, fcport); } } @@ -4980,6 +5006,21 @@ qla2x00_do_work(struct scsi_qla_host *vha) qla2x00_async_prlo_done(vha, e->u.logio.fcport, e->u.logio.data); break; + case QLA_EVT_GPNFT: + qla24xx_async_gpnft(vha, e->u.gpnft.fc4_type); + break; + case QLA_EVT_GPNFT_DONE: + qla24xx_async_gpnft_done(vha, e->u.iosb.sp); + break; + case QLA_EVT_GNNFT_DONE: + qla24xx_async_gnnft_done(vha, e->u.iosb.sp); + break; + case QLA_EVT_GNNID: + qla24xx_async_gnnid(vha, e->u.fcport.fcport); + break; + case QLA_EVT_GFPNID: + qla24xx_async_gfpnid(vha, e->u.fcport.fcport); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 3c25be73005d..d4ead404100c 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -969,7 +969,6 @@ static void qlt_free_session_done(struct work_struct *work) struct qla_hw_data *ha = vha->hw; unsigned long flags; bool logout_started = false; - struct event_arg ea; scsi_qla_host_t *base_vha; struct qlt_plogi_ack_t *own = sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]; @@ -1121,11 +1120,18 @@ static void qlt_free_session_done(struct work_struct *work) if (test_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags)) return; - if (!tgt || !tgt->tgt_stop) { - memset(&ea, 0, sizeof(ea)); - ea.event = FCME_DELETE_DONE; - ea.fcport = sess; - qla2x00_fcport_event_handler(vha, &ea); + if ((!tgt || !tgt->tgt_stop) && !LOOP_TRANSITION(vha)) { + switch (vha->host->active_mode) { + case MODE_INITIATOR: + case MODE_DUAL: + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + break; + case MODE_TARGET: + default: + /* no-op */ + break; + } } } @@ -4318,6 +4324,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, struct fc_port *sess; struct qla_tgt_cmd *cmd; unsigned long flags; + port_id_t id; if (unlikely(tgt->tgt_stop)) { ql_dbg(ql_dbg_io, vha, 0x3061, @@ -4325,6 +4332,12 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, return -EFAULT; } + id.b.al_pa = atio->u.isp24.fcp_hdr.s_id[2]; + id.b.area = atio->u.isp24.fcp_hdr.s_id[1]; + id.b.domain = atio->u.isp24.fcp_hdr.s_id[0]; + if (IS_SW_RESV_ADDR(id)) + return -EBUSY; + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, atio->u.isp24.fcp_hdr.s_id); if (unlikely(!sess)) { struct qla_tgt_sess_op *op = kzalloc(sizeof(struct qla_tgt_sess_op), @@ -4739,8 +4752,16 @@ static int qlt_handle_login(struct scsi_qla_host *vha, ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post new sess\n", __func__, __LINE__, iocb->u.isp24.port_name); - qla24xx_post_newsess_work(vha, &port_id, - iocb->u.isp24.port_name, pla); + if (iocb->u.isp24.status_subcode == ELS_PLOGI) + qla24xx_post_newsess_work(vha, &port_id, + iocb->u.isp24.port_name, + iocb->u.isp24.u.plogi.node_name, + pla, FC4_TYPE_UNKNOWN); + else + qla24xx_post_newsess_work(vha, &port_id, + iocb->u.isp24.port_name, NULL, + pla, FC4_TYPE_UNKNOWN); + goto out; } @@ -4869,6 +4890,11 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, break; } + if (IS_SW_RESV_ADDR(port_id)) { + res = 1; + break; + } + wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo); if (wwn) { @@ -4896,12 +4922,32 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, } if (sess != NULL) { + bool delete = false; spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); switch (sess->fw_login_state) { + case DSC_LS_PLOGI_PEND: case DSC_LS_PLOGI_COMP: case DSC_LS_PRLI_COMP: break; default: + delete = true; + break; + } + + switch (sess->disc_state) { + case DSC_LOGIN_PEND: + case DSC_GPDB: + case DSC_GPSC: + case DSC_UPD_FCPORT: + case DSC_LOGIN_COMPLETE: + case DSC_ADISC: + delete = false; + break; + default: + break; + } + + if (delete) { spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); /* diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index aba58d3848a6..8c04971a5454 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -993,7 +993,7 @@ struct qla_tgt_prm { /* Check for Switch reserved address */ #define IS_SW_RESV_ADDR(_s_id) \ - ((_s_id.b.domain == 0xff) && (_s_id.b.area == 0xfc)) + ((_s_id.b.domain == 0xff) && ((_s_id.b.area & 0xf0) == 0xf0)) #define QLA_TGT_XMIT_DATA 1 #define QLA_TGT_XMIT_STATUS 2 |