diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_target.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_target.c | 761 |
1 files changed, 429 insertions, 332 deletions
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 18069edd4773..fc89af8fe256 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -75,7 +75,8 @@ MODULE_PARM_DESC(ql2xuctrlirq, int ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE; -static int temp_sam_status = SAM_STAT_BUSY; +static int qla_sam_status = SAM_STAT_BUSY; +static int tc_sam_status = SAM_STAT_TASK_SET_FULL; /* target core */ /* * From scsi/fc/fc_fcp.h @@ -208,7 +209,7 @@ struct scsi_qla_host *qlt_find_host_by_d_id(struct scsi_qla_host *vha, host = btree_lookup32(&vha->hw->tgt.host_map, key); if (!host) - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf005, + ql_dbg(ql_dbg_tgt_mgt + ql_dbg_verbose, vha, 0xf005, "Unable to find host %06x\n", key); return host; @@ -309,17 +310,17 @@ static void qlt_try_to_dequeue_unknown_atios(struct scsi_qla_host *vha, host = qlt_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id); if (host != NULL) { - ql_dbg(ql_dbg_async, vha, 0x502f, + ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x502f, "Requeuing unknown ATIO_TYPE7 %p\n", u); qlt_24xx_atio_pkt(host, &u->atio, ha_locked); } else if (tgt->tgt_stop) { - ql_dbg(ql_dbg_async, vha, 0x503a, + ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x503a, "Freeing unknown %s %p, because tgt is being stopped\n", "ATIO_TYPE7", u); qlt_send_term_exchange(vha->hw->base_qpair, NULL, &u->atio, ha_locked, 0); } else { - ql_dbg(ql_dbg_async, vha, 0x503d, + ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x503d, "Reschedule u %p, vha %p, host %p\n", u, vha, host); if (!queued) { queued = 1; @@ -450,6 +451,7 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, ql_dbg(ql_dbg_tgt, vha, 0xe073, "qla_target(%d):%s: CRC2 Response pkt\n", vha->vp_idx, __func__); + /* fall through */ case CTIO_TYPE7: { struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt; @@ -606,7 +608,7 @@ void qla2x00_async_nack_sp_done(void *s, int res) __func__, __LINE__, sp->fcport->port_name, vha->fcport_count); - + sp->fcport->disc_state = DSC_UPD_FCPORT; qla24xx_post_upd_fcport_work(vha, sp->fcport); } else { ql_dbg(ql_dbg_disc, vha, 0x20f5, @@ -665,7 +667,7 @@ int qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport, qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2); sp->u.iocb_cmd.u.nack.ntfy = ntfy; - + sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout; sp->done = qla2x00_async_nack_sp_done; rval = qla2x00_start_sp(sp); @@ -861,7 +863,10 @@ void qlt_plogi_ack_unref(struct scsi_qla_host *vha, fcport->loop_id = loop_id; fcport->d_id = port_id; - qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PLOGI); + if (iocb->u.isp24.status_subcode == ELS_PLOGI) + qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PLOGI); + else + qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PRLI); list_for_each_entry(fcport, &vha->vp_fcports, list) { if (fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] == pla) @@ -890,6 +895,17 @@ qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla, iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0], pla->ref_count, pla, link); + if (link == QLT_PLOGI_LINK_CONFLICT) { + switch (sess->disc_state) { + case DSC_DELETED: + case DSC_DELETE_PEND: + pla->ref_count--; + return; + default: + break; + } + } + if (sess->plogi_link[link]) qlt_plogi_ack_unref(vha, sess->plogi_link[link]); @@ -954,8 +970,9 @@ 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]; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084, "%s: se_sess %p / sess %p from port %8phC loop_id %#04x" @@ -971,19 +988,35 @@ static void qlt_free_session_done(struct work_struct *work) logo.id = sess->d_id; logo.cmd_count = 0; + sess->send_els_logo = 0; qlt_send_first_logo(vha, &logo); } - if (sess->logout_on_delete) { + if (sess->logout_on_delete && sess->loop_id != FC_NO_LOOP_ID) { int rc; - rc = qla2x00_post_async_logout_work(vha, sess, NULL); - if (rc != QLA_SUCCESS) - ql_log(ql_log_warn, vha, 0xf085, - "Schedule logo failed sess %p rc %d\n", - sess, rc); - else - logout_started = true; + if (!own || + (own && + (own->iocb.u.isp24.status_subcode == ELS_PLOGI))) { + rc = qla2x00_post_async_logout_work(vha, sess, + NULL); + if (rc != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0xf085, + "Schedule logo failed sess %p rc %d\n", + sess, rc); + else + logout_started = true; + } else if (own && (own->iocb.u.isp24.status_subcode == + ELS_PRLI) && ha->flags.rida_fmt2) { + rc = qla2x00_post_async_prlo_work(vha, sess, + NULL); + if (rc != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0xf085, + "Schedule PRLO failed sess %p rc %d\n", + sess, rc); + else + logout_started = true; + } } } @@ -1007,7 +1040,7 @@ static void qlt_free_session_done(struct work_struct *work) } ql_dbg(ql_dbg_disc, vha, 0xf087, - "%s: sess %p logout completed\n",__func__, sess); + "%s: sess %p logout completed\n", __func__, sess); } if (sess->logo_ack_needed) { @@ -1033,8 +1066,7 @@ static void qlt_free_session_done(struct work_struct *work) sess->login_succ = 0; } - if (sess->chip_reset != ha->base_qpair->chip_reset) - qla2x00_clear_loop_id(sess); + qla2x00_clear_loop_id(sess); if (sess->conflict) { sess->conflict->login_pause = 0; @@ -1044,8 +1076,6 @@ static void qlt_free_session_done(struct work_struct *work) } { - struct qlt_plogi_ack_t *own = - sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]; struct qlt_plogi_ack_t *con = sess->plogi_link[QLT_PLOGI_LINK_CONFLICT]; struct imm_ntfy_from_isp *iocb; @@ -1076,6 +1106,7 @@ static void qlt_free_session_done(struct work_struct *work) sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL; } } + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, @@ -1089,14 +1120,24 @@ static void qlt_free_session_done(struct work_struct *work) wake_up_all(&vha->fcport_waitQ); base_vha = pci_get_drvdata(ha->pdev); + + sess->free_pending = 0; + 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; + } } } @@ -1104,11 +1145,20 @@ static void qlt_free_session_done(struct work_struct *work) void qlt_unreg_sess(struct fc_port *sess) { struct scsi_qla_host *vha = sess->vha; + unsigned long flags; ql_dbg(ql_dbg_disc, sess->vha, 0x210a, "%s sess %p for deletion %8phC\n", __func__, sess, sess->port_name); + spin_lock_irqsave(&sess->vha->work_lock, flags); + if (sess->free_pending) { + spin_unlock_irqrestore(&sess->vha->work_lock, flags); + return; + } + sess->free_pending = 1; + spin_unlock_irqrestore(&sess->vha->work_lock, flags); + if (sess->se_sess) vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); @@ -1175,10 +1225,10 @@ static void qla24xx_chk_fcp_state(struct fc_port *sess) } /* ha->tgt.sess_lock supposed to be held on entry */ -void qlt_schedule_sess_for_deletion(struct fc_port *sess, - bool immediate) +void qlt_schedule_sess_for_deletion(struct fc_port *sess) { struct qla_tgt *tgt = sess->tgt; + unsigned long flags; if (sess->disc_state == DSC_DELETE_PEND) return; @@ -1194,27 +1244,28 @@ void qlt_schedule_sess_for_deletion(struct fc_port *sess, return; } - sess->disc_state = DSC_DELETE_PEND; - if (sess->deleted == QLA_SESS_DELETED) sess->logout_on_delete = 0; + spin_lock_irqsave(&sess->vha->work_lock, flags); + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + spin_unlock_irqrestore(&sess->vha->work_lock, flags); + return; + } sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; + spin_unlock_irqrestore(&sess->vha->work_lock, flags); + + sess->disc_state = DSC_DELETE_PEND; + qla24xx_chk_fcp_state(sess); ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, "Scheduling sess %p for deletion\n", sess); - schedule_work(&sess->del_work); -} - -void qlt_schedule_sess_for_deletion_lock(struct fc_port *sess) -{ - unsigned long flags; - struct qla_hw_data *ha = sess->vha->hw; - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - qlt_schedule_sess_for_deletion(sess, 1); - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + /* use cancel to push work element through before re-queue */ + cancel_work_sync(&sess->del_work); + INIT_WORK(&sess->del_work, qla24xx_delete_sess_fn); + queue_work(sess->vha->hw->wq, &sess->del_work); } /* ha->tgt.sess_lock supposed to be held on entry */ @@ -1225,7 +1276,7 @@ static void qlt_clear_tgt_db(struct qla_tgt *tgt) list_for_each_entry(sess, &vha->vp_fcports, list) { if (sess->se_sess) - qlt_schedule_sess_for_deletion(sess, 1); + qlt_schedule_sess_for_deletion(sess); } /* At this point tgt could be already dead */ @@ -1400,7 +1451,7 @@ qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen) ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess); sess->local = 1; - qlt_schedule_sess_for_deletion(sess, false); + qlt_schedule_sess_for_deletion(sess); spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); } @@ -1560,8 +1611,11 @@ static void qlt_release(struct qla_tgt *tgt) btree_destroy64(&tgt->lun_qpair_map); - if (ha->tgt.tgt_ops && ha->tgt.tgt_ops->remove_target) - ha->tgt.tgt_ops->remove_target(vha); + if (vha->vp_idx) + if (ha->tgt.tgt_ops && + ha->tgt.tgt_ops->remove_target && + vha->vha_tgt.target_lport_ptr) + ha->tgt.tgt_ops->remove_target(vha); vha->vha_tgt.qla_tgt = NULL; @@ -1976,15 +2030,10 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf012, "qla_target(%d): task abort for non-existant session\n", vha->vp_idx); - rc = qlt_sched_sess_work(vha->vha_tgt.qla_tgt, - QLA_TGT_SESS_WORK_ABORT, abts, sizeof(*abts)); - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - if (rc != 0) { - qlt_24xx_send_abts_resp(ha->base_qpair, abts, - FCP_TMF_REJECTED, false); - } + qlt_24xx_send_abts_resp(ha->base_qpair, abts, FCP_TMF_REJECTED, + false); return; } spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); @@ -2174,7 +2223,7 @@ void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd) "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); + qlt_schedule_sess_for_deletion(mcmd->sess); } else { qlt_send_notify_ack(vha->hw->base_qpair, &mcmd->orig_iocb.imm_ntfy, 0, 0, 0, 0, 0, 0); @@ -3708,7 +3757,7 @@ static int qlt_term_ctio_exchange(struct qla_qpair *qpair, void *ctio, term = 1; if (term) - qlt_term_ctio_exchange(qpair, ctio, cmd, status); + qlt_send_term_exchange(qpair, cmd, &cmd->atio, 1, 0); return term; } @@ -3869,7 +3918,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, "%s %d %8phC post del sess\n", __func__, __LINE__, cmd->sess->port_name); - qlt_schedule_sess_for_deletion_lock(cmd->sess); + qlt_schedule_sess_for_deletion(cmd->sess); } break; } @@ -4204,76 +4253,6 @@ static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, return cmd; } -static void qlt_create_sess_from_atio(struct work_struct *work) -{ - struct qla_tgt_sess_op *op = container_of(work, - struct qla_tgt_sess_op, work); - scsi_qla_host_t *vha = op->vha; - struct qla_hw_data *ha = vha->hw; - struct fc_port *sess; - struct qla_tgt_cmd *cmd; - unsigned long flags; - uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id; - - spin_lock_irqsave(&vha->cmd_list_lock, flags); - list_del(&op->cmd_list); - spin_unlock_irqrestore(&vha->cmd_list_lock, flags); - - if (op->aborted) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf083, - "sess_op with tag %u is aborted\n", - op->atio.u.isp24.exchange_addr); - goto out_term; - } - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022, - "qla_target(%d): Unable to find wwn login" - " (s_id %x:%x:%x), trying to create it manually\n", - vha->vp_idx, s_id[0], s_id[1], s_id[2]); - - if (op->atio.u.raw.entry_count > 1) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023, - "Dropping multy entry atio %p\n", &op->atio); - goto out_term; - } - - sess = qlt_make_local_sess(vha, s_id); - /* sess has an extra creation ref. */ - - if (!sess) - goto out_term; - /* - * Now obtain a pre-allocated session tag using the original op->atio - * packet header, and dispatch into __qlt_do_work() using the existing - * process context. - */ - cmd = qlt_get_tag(vha, sess, &op->atio); - if (!cmd) { - struct qla_qpair *qpair = ha->base_qpair; - - spin_lock_irqsave(qpair->qp_lock_ptr, flags); - qlt_send_busy(qpair, &op->atio, SAM_STAT_BUSY); - spin_unlock_irqrestore(qpair->qp_lock_ptr, flags); - - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - ha->tgt.tgt_ops->put_sess(sess); - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - kfree(op); - return; - } - - /* - * __qlt_do_work() will call qlt_put_sess() to release - * the extra reference taken above by qlt_make_local_sess() - */ - __qlt_do_work(cmd); - kfree(op); - return; -out_term: - qlt_send_term_exchange(vha->hw->base_qpair, NULL, &op->atio, 0, 0); - kfree(op); -} - /* ha->hardware_lock supposed to be held on entry */ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, struct atio_from_isp *atio) @@ -4283,31 +4262,23 @@ 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, "New command while device %p is shutting down\n", tgt); - return -EFAULT; + return -ENODEV; } - 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), - GFP_ATOMIC); - if (!op) - return -ENOMEM; - - memcpy(&op->atio, atio, sizeof(*atio)); - op->vha = vha; - - spin_lock_irqsave(&vha->cmd_list_lock, flags); - list_add_tail(&op->cmd_list, &vha->qla_sess_op_cmd_list); - spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + 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; - INIT_WORK(&op->work, qlt_create_sess_from_atio); - queue_work(qla_tgt_wq, &op->work); - return 0; - } + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, atio->u.isp24.fcp_hdr.s_id); + if (unlikely(!sess)) + return -EFAULT; /* Another WWN used to have our s_id. Our PLOGI scheduled its * session deletion, but it's still in sess_del_work wq */ @@ -4336,7 +4307,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, spin_lock_irqsave(&ha->tgt.sess_lock, flags); ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - return -ENOMEM; + return -EBUSY; } cmd->cmd_in_wq = 1; @@ -4417,14 +4388,11 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) { struct atio_from_isp *a = (struct atio_from_isp *)iocb; struct qla_hw_data *ha = vha->hw; - struct qla_tgt *tgt; struct fc_port *sess; u64 unpacked_lun; int fn; unsigned long flags; - tgt = vha->vha_tgt.qla_tgt; - fn = a->u.isp24.fcp_cmnd.task_mgmt_flags; spin_lock_irqsave(&ha->tgt.sess_lock, flags); @@ -4435,15 +4403,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) unpacked_lun = scsilun_to_int((struct scsi_lun *)&a->u.isp24.fcp_cmnd.lun); - if (!sess) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf024, - "qla_target(%d): task mgmt fn 0x%x for " - "non-existant session\n", vha->vp_idx, fn); - return qlt_sched_sess_work(tgt, QLA_TGT_SESS_WORK_TM, iocb, - sizeof(struct atio_from_isp)); - } - - if (sess->deleted) + if (sess == NULL || sess->deleted) return -EFAULT; return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); @@ -4574,7 +4534,7 @@ qlt_find_sess_invalidate_other(scsi_qla_host_t *vha, uint64_t wwn, * might have cleared it when requested this session * deletion, so don't touch it */ - qlt_schedule_sess_for_deletion(other_sess, true); + qlt_schedule_sess_for_deletion(other_sess); } else { /* * Another wwn used to have our s_id/loop_id @@ -4584,11 +4544,10 @@ qlt_find_sess_invalidate_other(scsi_qla_host_t *vha, uint64_t wwn, "Invalidating sess %p loop_id %d wwn %llx.\n", other_sess, other_sess->loop_id, other_wwn); - other_sess->keep_nport_handle = 1; - *conflict_sess = other_sess; - qlt_schedule_sess_for_deletion(other_sess, - true); + if (other_sess->disc_state != DSC_DELETED) + *conflict_sess = other_sess; + qlt_schedule_sess_for_deletion(other_sess); } continue; } @@ -4602,7 +4561,7 @@ qlt_find_sess_invalidate_other(scsi_qla_host_t *vha, uint64_t wwn, /* Same loop_id but different s_id * Ok to kill and logout */ - qlt_schedule_sess_for_deletion(other_sess, true); + qlt_schedule_sess_for_deletion(other_sess); } } @@ -4652,6 +4611,138 @@ static int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id) return count; } +static int qlt_handle_login(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *iocb) +{ + struct fc_port *sess = NULL, *conflict_sess = NULL; + uint64_t wwn; + port_id_t port_id; + uint16_t loop_id, wd3_lo; + int res = 0; + struct qlt_plogi_ack_t *pla; + unsigned long flags; + + wwn = wwn_to_u64(iocb->u.isp24.port_name); + + 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); + + /* Mark all stale commands sitting in qla_tgt_wq for deletion */ + abort_cmds_for_s_id(vha, &port_id); + + if (wwn) { + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + sess = qlt_find_sess_invalidate_other(vha, wwn, + port_id, loop_id, &conflict_sess); + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + } + + if (IS_SW_RESV_ADDR(port_id)) { + res = 1; + goto out; + } + + pla = qlt_plogi_ack_find_add(vha, &port_id, iocb); + if (!pla) { + qlt_send_term_imm_notif(vha, iocb, 1); + goto out; + } + + if (conflict_sess) { + conflict_sess->login_gen++; + qlt_plogi_ack_link(vha, pla, conflict_sess, + QLT_PLOGI_LINK_CONFLICT); + } + + if (!sess) { + pla->ref_count++; + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post new sess\n", + __func__, __LINE__, iocb->u.isp24.port_name); + 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; + } + + qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN); + sess->d_id = port_id; + sess->login_gen++; + + if (iocb->u.isp24.status_subcode == ELS_PRLI) { + sess->fw_login_state = DSC_LS_PRLI_PEND; + sess->local = 0; + sess->loop_id = loop_id; + sess->d_id = port_id; + sess->fw_login_state = DSC_LS_PRLI_PEND; + wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo); + + if (wd3_lo & BIT_7) + sess->conf_compl_supported = 1; + + if ((wd3_lo & BIT_4) == 0) + sess->port_type = FCT_INITIATOR; + else + sess->port_type = FCT_TARGET; + + } else + sess->fw_login_state = DSC_LS_PLOGI_PEND; + + + ql_dbg(ql_dbg_disc, vha, 0x20f9, + "%s %d %8phC DS %d\n", + __func__, __LINE__, sess->port_name, sess->disc_state); + + switch (sess->disc_state) { + case DSC_DELETED: + qlt_plogi_ack_unref(vha, pla); + break; + + default: + /* + * Under normal circumstances we want to release nport handle + * during LOGO process to avoid nport handle leaks inside FW. + * The exception is when LOGO is done while another PLOGI with + * the same nport handle is waiting as might be the case here. + * Note: there is always a possibily of a race where session + * deletion has already started for other reasons (e.g. ACL + * removal) and now PLOGI arrives: + * 1. if PLOGI arrived in FW after nport handle has been freed, + * FW must have assigned this PLOGI a new/same handle and we + * 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, 0x20f9, + "%s %d %8phC post del sess\n", + __func__, __LINE__, sess->port_name); + + + qlt_schedule_sess_for_deletion(sess); + break; + } +out: + return res; +} + /* * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ @@ -4666,7 +4757,6 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, uint16_t loop_id; uint16_t wd3_lo; int res = 0; - struct qlt_plogi_ack_t *pla; unsigned long flags; wwn = wwn_to_u64(iocb->u.isp24.port_name); @@ -4690,88 +4780,32 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, */ switch (iocb->u.isp24.status_subcode) { case ELS_PLOGI: + res = qlt_handle_login(vha, iocb); + break; - /* Mark all stale commands in qla_tgt_wq for deletion */ - abort_cmds_for_s_id(vha, &port_id); - - if (wwn) { - spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); - sess = qlt_find_sess_invalidate_other(vha, wwn, - port_id, loop_id, &conflict_sess); - spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); - } - - if (IS_SW_RESV_ADDR(port_id)) { - res = 1; - break; - } - - pla = qlt_plogi_ack_find_add(vha, &port_id, iocb); - if (!pla) { - qlt_send_term_imm_notif(vha, iocb, 1); - break; - } - - res = 0; + case ELS_PRLI: + if (N2N_TOPO(ha)) { + sess = qla2x00_find_fcport_by_wwpn(vha, + iocb->u.isp24.port_name, 1); - if (conflict_sess) { - conflict_sess->login_gen++; - qlt_plogi_ack_link(vha, pla, conflict_sess, - QLT_PLOGI_LINK_CONFLICT); - } + if (sess && sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC Term PRLI due to PLOGI ACK not completed\n", + __func__, __LINE__, + iocb->u.isp24.port_name); + qlt_send_term_imm_notif(vha, iocb, 1); + break; + } - if (!sess) { - pla->ref_count++; - qla24xx_post_newsess_work(vha, &port_id, - iocb->u.isp24.port_name, pla); - res = 0; + res = qlt_handle_login(vha, iocb); break; } - qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN); - sess->fw_login_state = DSC_LS_PLOGI_PEND; - sess->d_id = port_id; - sess->login_gen++; - - switch (sess->disc_state) { - case DSC_DELETED: - qlt_plogi_ack_unref(vha, pla); - break; - - default: - /* - * Under normal circumstances we want to release nport handle - * during LOGO process to avoid nport handle leaks inside FW. - * The exception is when LOGO is done while another PLOGI with - * the same nport handle is waiting as might be the case here. - * Note: there is always a possibily of a race where session - * deletion has already started for other reasons (e.g. ACL - * removal) and now PLOGI arrives: - * 1. if PLOGI arrived in FW after nport handle has been freed, - * FW must have assigned this PLOGI a new/same handle and we - * 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, 0x20f9, - "%s %d %8phC post del sess\n", - __func__, __LINE__, sess->port_name); - - - qlt_schedule_sess_for_deletion_lock(sess); + if (IS_SW_RESV_ADDR(port_id)) { + res = 1; break; } - break; - - case ELS_PRLI: wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo); if (wwn) { @@ -4782,17 +4816,51 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, } if (conflict_sess) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09b, - "PRLI with conflicting sess %p port %8phC\n", - conflict_sess, conflict_sess->port_name); - qlt_send_term_imm_notif(vha, iocb, 1); - res = 0; - break; + switch (conflict_sess->disc_state) { + case DSC_DELETED: + case DSC_DELETE_PEND: + break; + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09b, + "PRLI with conflicting sess %p port %8phC\n", + conflict_sess, conflict_sess->port_name); + conflict_sess->fw_login_state = + DSC_LS_PORT_UNAVAIL; + qlt_send_term_imm_notif(vha, iocb, 1); + res = 0; + break; + } } if (sess != NULL) { - if (sess->fw_login_state != DSC_LS_PLOGI_PEND && - sess->fw_login_state != DSC_LS_PLOGI_COMP) { + 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); /* * Impatient initiator sent PRLI before last * PLOGI could finish. Will force him to re-try, @@ -4803,6 +4871,8 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, sess); qlt_send_term_imm_notif(vha, iocb, 1); res = 0; + spin_lock_irqsave(&tgt->ha->tgt.sess_lock, + flags); break; } @@ -4826,6 +4896,8 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, sess->port_type = FCT_INITIATOR; else sess->port_type = FCT_TARGET; + + spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); } res = 1; /* send notify ack */ @@ -4863,7 +4935,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, res = 1; break; } - /* drop through */ + /* fall through */ case ELS_LOGO: case ELS_PRLO: spin_lock_irqsave(&ha->tgt.sess_lock, flags); @@ -4892,7 +4964,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, } else { /* cmd did not go to upper layer. */ if (sess) { - qlt_schedule_sess_for_deletion_lock(sess); + qlt_schedule_sess_for_deletion(sess); res = 0; } /* else logo will be ack */ @@ -4930,6 +5002,10 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, break; } + ql_dbg(ql_dbg_disc, vha, 0xf026, + "qla_target(%d): Exit ELS opcode: 0x%02x res %d\n", + vha->vp_idx, iocb->u.isp24.status_subcode, res); + return res; } @@ -5320,7 +5396,6 @@ qlt_chk_qfull_thresh_hold(struct scsi_qla_host *vha, struct qla_qpair *qpair, struct atio_from_isp *atio, uint8_t ha_locked) { struct qla_hw_data *ha = vha->hw; - uint16_t status; unsigned long flags; if (ha->tgt.num_pend_cmds < Q_FULL_THRESH_HOLD(ha)) @@ -5328,8 +5403,7 @@ qlt_chk_qfull_thresh_hold(struct scsi_qla_host *vha, struct qla_qpair *qpair, if (!ha_locked) spin_lock_irqsave(&ha->hardware_lock, flags); - status = temp_sam_status; - qlt_send_busy(qpair, atio, status); + qlt_send_busy(qpair, atio, qla_sam_status); if (!ha_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -5344,7 +5418,7 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, struct qla_hw_data *ha = vha->hw; struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; int rc; - unsigned long flags; + unsigned long flags = 0; if (unlikely(tgt == NULL)) { ql_dbg(ql_dbg_tgt, vha, 0x3064, @@ -5368,8 +5442,7 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, "sending QUEUE_FULL\n", vha->vp_idx); if (!ha_locked) spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_busy(ha->base_qpair, atio, - SAM_STAT_TASK_SET_FULL); + qlt_send_busy(ha->base_qpair, atio, qla_sam_status); if (!ha_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -5388,42 +5461,37 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, rc = qlt_handle_task_mgmt(vha, atio); } if (unlikely(rc != 0)) { - if (rc == -ESRCH) { - if (!ha_locked) - spin_lock_irqsave(&ha->hardware_lock, - flags); - -#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ - qlt_send_busy(ha->base_qpair, atio, - SAM_STAT_BUSY); -#else + if (!ha_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + switch (rc) { + case -ENODEV: + ql_dbg(ql_dbg_tgt, vha, 0xe05f, + "qla_target: Unable to send command to target\n"); + break; + case -EBADF: + ql_dbg(ql_dbg_tgt, vha, 0xe05f, + "qla_target: Unable to send command to target, sending TERM EXCHANGE for rsp\n"); qlt_send_term_exchange(ha->base_qpair, NULL, atio, 1, 0); -#endif - if (!ha_locked) - spin_unlock_irqrestore( - &ha->hardware_lock, flags); - } else { - if (tgt->tgt_stop) { - ql_dbg(ql_dbg_tgt, vha, 0xe059, - "qla_target: Unable to send " - "command to target for req, " - "ignoring.\n"); - } else { - ql_dbg(ql_dbg_tgt, vha, 0xe05a, - "qla_target(%d): Unable to send " - "command to target, sending BUSY " - "status.\n", vha->vp_idx); - if (!ha_locked) - spin_lock_irqsave( - &ha->hardware_lock, flags); - qlt_send_busy(ha->base_qpair, - atio, SAM_STAT_BUSY); - if (!ha_locked) - spin_unlock_irqrestore( - &ha->hardware_lock, flags); - } + break; + case -EBUSY: + ql_dbg(ql_dbg_tgt, vha, 0xe060, + "qla_target(%d): Unable to send command to target, sending BUSY status\n", + vha->vp_idx); + qlt_send_busy(ha->base_qpair, atio, + tc_sam_status); + break; + default: + ql_dbg(ql_dbg_tgt, vha, 0xe060, + "qla_target(%d): Unable to send command to target, sending BUSY status\n", + vha->vp_idx); + qlt_send_busy(ha->base_qpair, atio, + qla_sam_status); + break; } + if (!ha_locked) + spin_unlock_irqrestore(&ha->hardware_lock, + flags); } break; @@ -5506,27 +5574,31 @@ static void qlt_response_pkt(struct scsi_qla_host *vha, rc = qlt_handle_cmd_for_atio(vha, atio); if (unlikely(rc != 0)) { - if (rc == -ESRCH) { -#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ - qlt_send_busy(rsp->qpair, atio, 0); -#else - qlt_send_term_exchange(rsp->qpair, NULL, atio, 1, 0); -#endif - } else { - if (tgt->tgt_stop) { - ql_dbg(ql_dbg_tgt, vha, 0xe05f, - "qla_target: Unable to send " - "command to target, sending TERM " - "EXCHANGE for rsp\n"); - qlt_send_term_exchange(rsp->qpair, NULL, - atio, 1, 0); - } else { - ql_dbg(ql_dbg_tgt, vha, 0xe060, - "qla_target(%d): Unable to send " - "command to target, sending BUSY " - "status\n", vha->vp_idx); - qlt_send_busy(rsp->qpair, atio, 0); - } + switch (rc) { + case -ENODEV: + ql_dbg(ql_dbg_tgt, vha, 0xe05f, + "qla_target: Unable to send command to target\n"); + break; + case -EBADF: + ql_dbg(ql_dbg_tgt, vha, 0xe05f, + "qla_target: Unable to send command to target, sending TERM EXCHANGE for rsp\n"); + qlt_send_term_exchange(rsp->qpair, NULL, + atio, 1, 0); + break; + case -EBUSY: + ql_dbg(ql_dbg_tgt, vha, 0xe060, + "qla_target(%d): Unable to send command to target, sending BUSY status\n", + vha->vp_idx); + qlt_send_busy(rsp->qpair, atio, + tc_sam_status); + break; + default: + ql_dbg(ql_dbg_tgt, vha, 0xe060, + "qla_target(%d): Unable to send command to target, sending BUSY status\n", + vha->vp_idx); + qlt_send_busy(rsp->qpair, atio, + qla_sam_status); + break; } } } @@ -5755,7 +5827,7 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, unsigned long flags; u8 newfcport = 0; - fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (!fcport) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f, "qla_target(%d): Allocation of tmp FC port failed", @@ -5784,6 +5856,7 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, tfcp->port_type = fcport->port_type; tfcp->supported_classes = fcport->supported_classes; tfcp->flags |= fcport->flags; + tfcp->scan_state = QLA_FCPORT_FOUND; del = fcport; fcport = tfcp; @@ -6445,18 +6518,21 @@ qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha) qlt_add_target(ha, vha); } -void -qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req) +u8 +qlt_rff_id(struct scsi_qla_host *vha) { + u8 fc4_feature = 0; /* * FC-4 Feature bit 0 indicates target functionality to the name server. */ if (qla_tgt_mode_enabled(vha)) { - ct_req->req.rff_id.fc4_feature = BIT_0; + fc4_feature = BIT_0; } else if (qla_ini_mode_enabled(vha)) { - ct_req->req.rff_id.fc4_feature = BIT_1; + fc4_feature = BIT_1; } else if (qla_dual_mode_enabled(vha)) - ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; + fc4_feature = BIT_0 | BIT_1; + + return fc4_feature; } /* @@ -6546,7 +6622,9 @@ void qlt_24xx_config_rings(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; - struct init_cb_24xx *icb; + struct qla_msix_entry *msix = &ha->msix_entries[2]; + struct init_cb_24xx *icb = (struct init_cb_24xx *)ha->init_cb; + if (!QLA_TGT_MODE_ENABLED()) return; @@ -6554,19 +6632,28 @@ qlt_24xx_config_rings(struct scsi_qla_host *vha) WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), 0); RD_REG_DWORD(ISP_ATIO_Q_OUT(vha)); - icb = (struct init_cb_24xx *)ha->init_cb; - - if ((ql2xenablemsix != 0) && IS_ATIO_MSIX_CAPABLE(ha)) { - struct qla_msix_entry *msix = &ha->msix_entries[2]; - - icb->msix_atio = cpu_to_le16(msix->entry); - ql_dbg(ql_dbg_init, vha, 0xf072, - "Registering ICB vector 0x%x for atio que.\n", - msix->entry); - } else if (ql2xenablemsix == 0) { - icb->firmware_options_2 |= cpu_to_le32(BIT_26); - ql_dbg(ql_dbg_init, vha, 0xf07f, - "Registering INTx vector for ATIO.\n"); + if (ha->flags.msix_enabled) { + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { + if (IS_QLA2071(ha)) { + /* 4 ports Baker: Enable Interrupt Handshake */ + icb->msix_atio = 0; + icb->firmware_options_2 |= BIT_26; + } else { + icb->msix_atio = cpu_to_le16(msix->entry); + icb->firmware_options_2 &= ~BIT_26; + } + ql_dbg(ql_dbg_init, vha, 0xf072, + "Registering ICB vector 0x%x for atio que.\n", + msix->entry); + } + } else { + /* INTx|MSI */ + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { + icb->msix_atio = 0; + icb->firmware_options_2 |= BIT_26; + ql_dbg(ql_dbg_init, vha, 0xf072, + "%s: Use INTx for ATIOQ.\n", __func__); + } } } @@ -6574,6 +6661,7 @@ void qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) { struct qla_hw_data *ha = vha->hw; + u32 tmp; if (!QLA_TGT_MODE_ENABLED()) return; @@ -6625,6 +6713,14 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) nv->firmware_options_1 &= cpu_to_le32(~BIT_15); /* Enable target PRLI control */ nv->firmware_options_2 |= cpu_to_le32(BIT_14); + + if (IS_QLA25XX(ha)) { + /* Change Loop-prefer to Pt-Pt */ + tmp = ~(BIT_4|BIT_5|BIT_6); + nv->firmware_options_2 &= cpu_to_le32(tmp); + tmp = P2P << 4; + nv->firmware_options_2 |= cpu_to_le32(tmp); + } } else { if (ha->tgt.saved_set) { nv->exchange_count = ha->tgt.saved_exchange_count; @@ -6679,6 +6775,7 @@ void qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) { struct qla_hw_data *ha = vha->hw; + u32 tmp; if (!QLA_TGT_MODE_ENABLED()) return; @@ -6729,6 +6826,12 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) nv->host_p &= cpu_to_le32(~BIT_10); /* Enable target PRLI control */ nv->firmware_options_2 |= cpu_to_le32(BIT_14); + + /* Change Loop-prefer to Pt-Pt */ + tmp = ~(BIT_4|BIT_5|BIT_6); + nv->firmware_options_2 &= cpu_to_le32(tmp); + tmp = P2P << 4; + nv->firmware_options_2 |= cpu_to_le32(tmp); } else { if (ha->tgt.saved_set) { nv->exchange_count = ha->tgt.saved_exchange_count; @@ -6991,20 +7094,14 @@ qlt_update_vp_map(struct scsi_qla_host *vha, int cmd) void qlt_update_host_map(struct scsi_qla_host *vha, port_id_t id) { - unsigned long flags; - struct qla_hw_data *ha = vha->hw; if (!vha->d_id.b24) { - spin_lock_irqsave(&ha->vport_slock, flags); vha->d_id = id; qlt_update_vp_map(vha, SET_AL_PA); - spin_unlock_irqrestore(&ha->vport_slock, flags); } else if (vha->d_id.b24 != id.b24) { - spin_lock_irqsave(&ha->vport_slock, flags); qlt_update_vp_map(vha, RESET_AL_PA); vha->d_id = id; qlt_update_vp_map(vha, SET_AL_PA); - spin_unlock_irqrestore(&ha->vport_slock, flags); } } |