diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index b01237b58..e2ba96e3e 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -120,18 +120,6 @@ for more details why. module, otherwise your initiators could not see the target, when it is enabled after qla2x00tgt module load. -3. If you delete and then add back with the same WWN an NPIV initiator -on the initiator side, make sure it has the same port_id as well. In -Fibre Channel initiators identified by port_id (s_id in FC terms), so if -the recreated NPIV initiator has another port_id, which was already used -by another (NPIV) initiator, those initiators could be confused by the -target and assigned to incorrect security groups, hence they could see -incorrect LUNs. - -If you can't ensure the same port_id's for recreated initiators, it is -safer to restart qla2x00tgt and qla2xxx modules on the target to make -sure the target doesn't have any initiator port_id cached. - Initiator and target modes -------------------------- diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index c64c04ceb..5fa9409e0 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -265,12 +265,11 @@ static inline void q2t_sess_put(struct q2t_sess *sess) /* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt, - uint16_t lid) + uint16_t loop_id) { struct q2t_sess *sess; - sBUG_ON(tgt == NULL); list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { - if (lid == (sess->loop_id)) + if ((loop_id == sess->loop_id) && !sess->deleted) return sess; } return NULL; @@ -281,11 +280,11 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt, const uint8_t *s_id) { struct q2t_sess *sess; - sBUG_ON(tgt == NULL); list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { if ((sess->s_id.b.al_pa == s_id[2]) && (sess->s_id.b.area == s_id[1]) && - (sess->s_id.b.domain == s_id[0])) + (sess->s_id.b.domain == s_id[0]) && + !sess->deleted) return sess; } return NULL; @@ -296,11 +295,30 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt, const uint8_t *s_id) { struct q2t_sess *sess; - sBUG_ON(tgt == NULL); list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { if ((sess->s_id.b.al_pa == s_id[0]) && (sess->s_id.b.area == s_id[1]) && - (sess->s_id.b.domain == s_id[2])) + (sess->s_id.b.domain == s_id[2]) && + !sess->deleted) + return sess; + } + return NULL; +} + +/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +static inline struct q2t_sess *q2t_find_sess_by_port_name(struct q2t_tgt *tgt, + const uint8_t *port_name) +{ + struct q2t_sess *sess; + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + if ((sess->port_name[0] == port_name[0]) && + (sess->port_name[1] == port_name[1]) && + (sess->port_name[2] == port_name[2]) && + (sess->port_name[3] == port_name[3]) && + (sess->port_name[4] == port_name[4]) && + (sess->port_name[5] == port_name[5]) && + (sess->port_name[6] == port_name[6]) && + (sess->port_name[7] == port_name[7])) return sess; } return NULL; @@ -653,6 +671,7 @@ static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd) if (loop_id == 0xFFFF) { /* Global event */ + atomic_inc(&ha->tgt->tgt_global_resets_count); q2t_clear_tgt_db(ha->tgt, 1); if (!list_empty(&ha->tgt->sess_list)) { sess = list_entry(ha->tgt->sess_list.next, @@ -729,10 +748,9 @@ static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess) sess->deleted = 1; sess->expires = jiffies + dev_loss_tmo * HZ; - PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:%02x:%02x:" + PRINT_INFO("qla2x00t(%ld): session for port %02x:%02x:%02x:" "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for " "deletion in %d secs", tgt->ha->instance, - sess->local ? "local " : "", sess->port_name[0], sess->port_name[1], sess->port_name[2], sess->port_name[3], sess->port_name[4], sess->port_name[5], @@ -851,15 +869,120 @@ out: return res; } -static bool q2t_check_fcport_exist(scsi_qla_host_t *ha, const uint8_t *s_id) +static bool q2t_check_fcport_exist(scsi_qla_host_t *ha, struct q2t_sess *sess) { - bool res; - uint16_t loop_id; + bool res, found = false; + int rc, i; + uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */ + uint16_t entries; + void *pmap; + int pmap_len; + fc_port_t *fcport; + int global_resets; TRACE_ENTRY(); - res = (q24_get_loop_id(ha, s_id, &loop_id) == 0); +retry: + global_resets = atomic_read(&ha->tgt->tgt_global_resets_count); + rc = qla2x00_get_node_name_list(ha, &pmap, &pmap_len); + if (rc != QLA_SUCCESS) { + res = false; + goto out; + } + + if (IS_FWI2_CAPABLE(ha)) { + struct qla_port24_data *pmap24 = pmap; + + entries = pmap_len/sizeof(*pmap24); + + for (i = 0; i < entries; ++i) { + if ((sess->port_name[0] == pmap24[i].port_name[0]) && + (sess->port_name[1] == pmap24[i].port_name[1]) && + (sess->port_name[2] == pmap24[i].port_name[2]) && + (sess->port_name[3] == pmap24[i].port_name[3]) && + (sess->port_name[4] == pmap24[i].port_name[4]) && + (sess->port_name[5] == pmap24[i].port_name[5]) && + (sess->port_name[6] == pmap24[i].port_name[6]) && + (sess->port_name[7] == pmap24[i].port_name[7])) { + loop_id = le16_to_cpu(pmap24[i].loop_id); + found = true; + break; + } + } + } else { + struct qla_port23_data *pmap2x = pmap; + + entries = pmap_len/sizeof(*pmap2x); + + for (i = 0; i < entries; ++i) { + if ((sess->port_name[0] == pmap2x[i].port_name[0]) && + (sess->port_name[1] == pmap2x[i].port_name[1]) && + (sess->port_name[2] == pmap2x[i].port_name[2]) && + (sess->port_name[3] == pmap2x[i].port_name[3]) && + (sess->port_name[4] == pmap2x[i].port_name[4]) && + (sess->port_name[5] == pmap2x[i].port_name[5]) && + (sess->port_name[6] == pmap2x[i].port_name[6]) && + (sess->port_name[7] == pmap2x[i].port_name[7])) { + loop_id = le16_to_cpu(pmap2x[i].loop_id); + found = true; + break; + } + } + } + + kfree(pmap); + + if (!found) { + res = false; + goto out; + } + + TRACE_MGMT_DBG("loop_id %d", loop_id); + + fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); + if (fcport == NULL) { + PRINT_ERROR("qla2x00t(%ld): Allocation of tmp FC port failed", + ha->instance); + res = false; + goto out; + } + + fcport->loop_id = loop_id; + + rc = qla2x00_get_port_database(ha, fcport, 0); + if (rc != QLA_SUCCESS) { + PRINT_ERROR("qla2x00t(%ld): Failed to retrieve fcport " + "information -- get_port_database() returned %x " + "(loop_id=0x%04x)", ha->instance, rc, loop_id); + res = false; + goto out_free_fcport; + } + + if (global_resets != atomic_read(&ha->tgt->tgt_global_resets_count)) { + TRACE_MGMT_DBG("qla2x00t(%ld): global reset during session " + "discovery (counter was %d, new %d), retrying", + ha->instance, global_resets, + atomic_read(&ha->tgt->tgt_global_resets_count)); + goto retry; + } + + TRACE_MGMT_DBG("Updating sess %p s_id %x:%x:%x, " + "loop_id %d) to d_id %x:%x:%x, loop_id %d", sess, + sess->s_id.b.domain, sess->s_id.b.al_pa, + sess->s_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); + + sess->s_id = fcport->d_id; + sess->loop_id = fcport->loop_id; + sess->conf_compl_supported = fcport->conf_compl_supported; + + res = true; + +out_free_fcport: + kfree(fcport); + +out: TRACE_EXIT_RES(res); return res; } @@ -894,10 +1017,18 @@ static void q2t_del_sess_work_fn(struct delayed_work *work) q2t_undelete_sess(sess); spin_unlock_irqrestore(&pha->hardware_lock, flags); - cancel = q2t_check_fcport_exist(ha, (uint8_t *)&sess->s_id); + cancel = q2t_check_fcport_exist(ha, sess); spin_lock_irqsave(&pha->hardware_lock, flags); if (cancel) { + if (sess->deleted) { + /* + * sess was again deleted while we were + * discovering it + */ + continue; + } + PRINT_INFO("qla2x00t(%ld): cancel deletion of " "session for port %02x:%02x:%02x:" "%02x:%02x:%02x:%02x:%02x (loop ID %d), " @@ -1028,8 +1159,9 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, goto out_free_sess_wwn; } - spin_lock_irq(&pha->hardware_lock); TRACE_MGMT_DBG("Adding sess %p to tgt %p", sess, tgt); + + spin_lock_irq(&pha->hardware_lock); list_add_tail(&sess->sess_list_entry, &tgt->sess_list); tgt->sess_count++; spin_unlock_irq(&pha->hardware_lock); @@ -1037,7 +1169,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, PRINT_INFO("qla2x00t(%ld): %ssession for wwn %s (loop_id %d, " "s_id %x:%x:%x, confirmed completion %ssupported) added", ha->instance, local ? "local " : "", wwn_str, fcport->loop_id, - sess->s_id.b.domain, sess->s_id.b.al_pa, sess->s_id.b.area, + sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa, sess->conf_compl_supported ? "" : "not "); kfree(wwn_str); @@ -1056,21 +1188,6 @@ out_free_sess: goto out; } -/* pha->hardware_lock supposed to be held on entry */ -static void q2t_reappear_sess(struct q2t_sess *sess, const char *reason) -{ - q2t_undelete_sess(sess); - - PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:" - "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) " - "reappeared%s", sess->tgt->ha->instance, - sess->local ? "local " : "", sess->port_name[0], - sess->port_name[1], sess->port_name[2], sess->port_name[3], - sess->port_name[4], sess->port_name[5], sess->port_name[6], - sess->port_name[7], sess->loop_id, reason); - TRACE_MGMT_DBG("Appeared sess %p", sess); -} - static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) { struct q2t_tgt *tgt; @@ -1091,7 +1208,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) spin_lock_irq(&pha->hardware_lock); - sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id); + sess = q2t_find_sess_by_port_name(tgt, fcport->port_name); if (sess == NULL) { spin_unlock_irq(&pha->hardware_lock); sess = q2t_create_sess(ha, fcport, false); @@ -1099,8 +1216,23 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) if (sess != NULL) q2t_sess_put(sess); /* put the extra creation ref */ } else { - if (sess->deleted) - q2t_reappear_sess(sess, ""); + if (sess->deleted) { + q2t_undelete_sess(sess); + + PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:" + "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) " + "reappeared", sess->tgt->ha->instance, + sess->local ? "local " : "", sess->port_name[0], + sess->port_name[1], sess->port_name[2], + sess->port_name[3], sess->port_name[4], + sess->port_name[5], sess->port_name[6], + sess->port_name[7], sess->loop_id); + + TRACE_MGMT_DBG("Reappeared sess %p", sess); + } + sess->s_id = fcport->d_id; + sess->loop_id = fcport->loop_id; + sess->conf_compl_supported = fcport->conf_compl_supported; } if (sess->local) { @@ -1144,7 +1276,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) spin_lock_irq(&pha->hardware_lock); - sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id); + sess = q2t_find_sess_by_port_name(tgt, fcport->port_name); if (sess == NULL) goto out_unlock_ha; @@ -1270,6 +1402,49 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) return 0; } +/* pha->hardware_lock supposed to be held on entry */ +static int q2t_sched_sess_work(struct q2t_tgt *tgt, int type, + const void *param, unsigned int param_size) +{ + int res; + struct q2t_sess_work_param *prm; + unsigned long flags; + + TRACE_ENTRY(); + + prm = kzalloc(sizeof(*prm), GFP_ATOMIC); + if (prm == NULL) { + PRINT_ERROR("qla2x00t(%ld): Unable to create session " + "work, command will be refused", tgt->ha->instance); + res = -ENOMEM; + goto out; + } + + TRACE_MGMT_DBG("Scheduling work (type %d, prm %p) to find session for " + "param %p (size %d, tgt %p)", type, prm, param, param_size, tgt); + + sBUG_ON(param_size > (sizeof(*prm) - + offsetof(struct q2t_sess_work_param, cmd))); + + prm->type = type; + memcpy(&prm->cmd, param, param_size); + + spin_lock_irqsave(&tgt->sess_work_lock, flags); + if (!tgt->sess_works_pending) + tgt->tm_to_unknown = 0; + list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list); + tgt->sess_works_pending = 1; + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); + + schedule_work(&tgt->sess_work); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + /* * pha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ @@ -1520,14 +1695,55 @@ out: return; } +/* pha->hardware_lock supposed to be held on entry */ +static int __q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts, + struct q2t_sess *sess) +{ + int res; + uint32_t tag = abts->exchange_addr_to_abort; + struct q2t_mgmt_cmd *mcmd; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("qla2x00t(%ld): task abort (tag=%d)", ha->instance, + tag); + + mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); + if (mcmd == NULL) { + PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed", + ha->instance, __func__); + res = -ENOMEM; + goto out; + } + memset(mcmd, 0, sizeof(*mcmd)); + + mcmd->sess = sess; + memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts)); + + res = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag, + SCST_ATOMIC, mcmd); + if (res != 0) { + PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d", + ha->instance, res); + goto out_free; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_free: + mempool_free(mcmd, q2t_mgmt_cmd_mempool); + goto out; +} + /* * pha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts) { - uint32_t tag; int rc; - struct q2t_mgmt_cmd *mcmd; + uint32_t tag = abts->exchange_addr_to_abort; struct q2t_sess *sess; TRACE_ENTRY(); @@ -1538,8 +1754,6 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts) goto out_err; } - tag = abts->exchange_addr_to_abort; - if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) { TRACE_MGMT_DBG("qla2x00t(%ld): ABTS: Unknown Exchange " "Address received", ha->instance); @@ -1553,38 +1767,28 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts) sess = q2t_find_sess_by_s_id_le(ha->tgt, abts->fcp_hdr_le.s_id); if (sess == NULL) { - TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort for unexisting " + TRACE_MGMT_DBG("qla2x00t(%ld): task abort for unexisting " "session", ha->instance); - ha->tgt->tm_to_unknown = 1; - goto out_err; + rc = q2t_sched_sess_work(ha->tgt, Q2T_SESS_WORK_ABORT, abts, + sizeof(*abts)); + if (rc != 0) { + ha->tgt->tm_to_unknown = 1; + goto out_err; + } + goto out; } - mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); - if (mcmd == NULL) { - PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed", - ha->instance, __func__); - goto out_err; - } - memset(mcmd, 0, sizeof(*mcmd)); - - mcmd->sess = sess; - memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts)); - - rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag, - SCST_ATOMIC, mcmd); + rc = __q24_handle_abts(ha, abts, sess); if (rc != 0) { PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d", ha->instance, rc); - goto out_err_free; + goto out_err; } out: TRACE_EXIT(); return; -out_err_free: - mempool_free(mcmd, q2t_mgmt_cmd_mempool); - out_err: q24_send_abts_resp(ha, abts, SCST_MGMT_STATUS_REJECTED, false); goto out; @@ -3560,9 +3764,6 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) } } - if (unlikely(sess->deleted)) - q2t_reappear_sess(sess, " by new commands"); - res = q2t_do_send_cmd_to_scst(ha, cmd, sess); if (unlikely(res != 0)) goto out_free_cmd; @@ -3576,32 +3777,14 @@ out_free_cmd: goto out; out_sched: - { - struct q2t_sess_work_param *prm; - unsigned long flags; - - prm = kzalloc(sizeof(*prm), GFP_ATOMIC); - if (prm == NULL) { - PRINT_ERROR("qla2x00t(%ld): Unable to create session " - "work, command will be refused", ha->instance); - res = -1; - goto out_free_cmd; - } - - TRACE_MGMT_DBG("Scheduling work to find session for cmd %p", - cmd); - - prm->cmd = cmd; - - spin_lock_irqsave(&tgt->sess_work_lock, flags); - if (!tgt->sess_works_pending) - tgt->tm_to_unknown = 0; - list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list); - tgt->sess_works_pending = 1; - spin_unlock_irqrestore(&tgt->sess_work_lock, flags); - - schedule_work(&tgt->sess_work); + if (atio->entry_count > 1) { + TRACE_MGMT_DBG("Dropping multy entry cmd %p", cmd); + res = -EBUSY; + goto out_free_cmd; } + res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_CMD, &cmd, sizeof(cmd)); + if (res != 0) + goto out_free_cmd; goto out; } @@ -3740,11 +3923,6 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb) lun_size = sizeof(a->fcp_cmnd.lun); fn = a->fcp_cmnd.task_mgmt_flags; sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id); - if (sess != NULL) { - sess->s_id.b.al_pa = a->fcp_hdr.s_id[2]; - sess->s_id.b.area = a->fcp_hdr.s_id[1]; - sess->s_id.b.domain = a->fcp_hdr.s_id[0]; - } } else { notify_entry_t *n = (notify_entry_t *)iocb; /* make it be in network byte order */ @@ -3756,10 +3934,13 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb) } if (sess == NULL) { - TRACE(TRACE_MGMT, "qla2x00t(%ld): task mgmt fn 0x%x for " + TRACE_MGMT_DBG("qla2x00t(%ld): task mgmt fn 0x%x for " "non-existant session", ha->instance, fn); - tgt->tm_to_unknown = 1; - res = -ESRCH; + res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_TM, iocb, + IS_FWI2_CAPABLE(ha) ? sizeof(atio7_entry_t) : + sizeof(notify_entry_t)); + if (res != 0) + tgt->tm_to_unknown = 1; goto out; } @@ -3771,28 +3952,14 @@ out: } /* pha->hardware_lock supposed to be held on entry */ -static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb) +static int __q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb, + struct q2t_sess *sess) { - int res = 0, rc; + int res, rc; struct q2t_mgmt_cmd *mcmd; - struct q2t_sess *sess; - int loop_id; - uint32_t tag; TRACE_ENTRY(); - loop_id = GET_TARGET_ID(ha, iocb); - tag = le16_to_cpu(iocb->seq_id); - - sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); - if (sess == NULL) { - TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort for unexisting " - "session", ha->instance); - ha->tgt->tm_to_unknown = 1; - res = -EFAULT; - goto out; - } - mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); if (mcmd == NULL) { PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed", @@ -3806,8 +3973,8 @@ static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb) memcpy(&mcmd->orig_iocb.notify_entry, iocb, sizeof(mcmd->orig_iocb.notify_entry)); - rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag, - SCST_ATOMIC, mcmd); + rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, + le16_to_cpu(iocb->seq_id), SCST_ATOMIC, mcmd); if (rc != 0) { PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d", ha->instance, rc); @@ -3815,6 +3982,8 @@ static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb) goto out_free; } + res = 0; + out: TRACE_EXIT_RES(res); return res; @@ -3824,6 +3993,35 @@ out_free: goto out; } +/* pha->hardware_lock supposed to be held on entry */ +static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb) +{ + int res; + struct q2t_sess *sess; + int loop_id; + + TRACE_ENTRY(); + + loop_id = GET_TARGET_ID(ha, iocb); + + sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); + if (sess == NULL) { + TRACE_MGMT_DBG("qla2x00t(%ld): task abort for unexisting " + "session", ha->instance); + res = q2t_sched_sess_work(sess->tgt, Q2T_SESS_WORK_ABORT, iocb, + sizeof(*iocb)); + if (res != 0) + sess->tgt->tm_to_unknown = 1; + goto out; + } + + res = __q2t_abort_task(ha, iocb, sess); + +out: + TRACE_EXIT_RES(res); + return res; +} + /* * pha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ @@ -5093,39 +5291,38 @@ out: } /* Must be called under tgt_mutex */ -static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio) +static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, + const uint8_t *s_id, uint16_t loop_id) { struct q2t_sess *sess = NULL; fc_port_t *fcport = NULL; - uint16_t loop_id = 0xFFFF; /* to remove warning */ - int rc; + int rc, global_resets; TRACE_ENTRY(); +retry: + global_resets = atomic_read(&ha->tgt->tgt_global_resets_count); + if (IS_FWI2_CAPABLE(ha)) { - atio7_entry_t *atio7 = (atio7_entry_t *)atio; - rc = q24_get_loop_id(ha, atio7->fcp_hdr.s_id, &loop_id); + rc = q24_get_loop_id(ha, s_id, &loop_id); if (rc != 0) { - if ((atio7->fcp_hdr.s_id[0] == 0xFF) && - (atio7->fcp_hdr.s_id[1] == 0xFC)) { + if ((s_id[0] == 0xFF) && + (s_id[1] == 0xFC)) { /* * This is Domain Controller, so it should be * OK to drop SCSI commands from it. */ TRACE_MGMT_DBG("Unable to find initiator with " - "S_ID %x:%x:%x", atio7->fcp_hdr.s_id[0], - atio7->fcp_hdr.s_id[1], - atio7->fcp_hdr.s_id[2]); + "S_ID %x:%x:%x", s_id[0], s_id[1], + s_id[2]); } else PRINT_ERROR("qla2x00t(%ld): Unable to find " "initiator with S_ID %x:%x:%x", - ha->instance, atio7->fcp_hdr.s_id[0], - atio7->fcp_hdr.s_id[1], - atio7->fcp_hdr.s_id[2]); + ha->instance, s_id[0], s_id[1], + s_id[2]); goto out; } - } else - loop_id = GET_TARGET_ID(ha, (atio_entry_t *)atio); + } fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); if (fcport == NULL) { @@ -5146,6 +5343,14 @@ static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio) goto out_free_fcport; } + if (global_resets != atomic_read(&ha->tgt->tgt_global_resets_count)) { + TRACE_MGMT_DBG("qla2x00t(%ld): global reset during session " + "discovery (counter was %d, new %d), retrying", + ha->instance, global_resets, + atomic_read(&ha->tgt->tgt_global_resets_count)); + goto retry; + } + sess = q2t_create_sess(ha, fcport, true); out_free_fcport: @@ -5156,19 +5361,19 @@ out: return sess; } -static int q2t_exec_sess_work(struct q2t_tgt *tgt, +static void q2t_exec_sess_work(struct q2t_tgt *tgt, struct q2t_sess_work_param *prm) { scsi_qla_host_t *ha = tgt->ha; scsi_qla_host_t *pha = to_qla_parent(ha); - int res = 0; + int rc; struct q2t_sess *sess = NULL; - struct q2t_cmd *cmd = prm->cmd; - atio_t *atio = (atio_t *)&cmd->atio; + const uint8_t *s_id = NULL; /* to hide compiler warnings */ + int loop_id = -1; /* to hide compiler warnings */ TRACE_ENTRY(); - TRACE_MGMT_DBG("cmd %p", cmd); + TRACE_MGMT_DBG("prm %p", prm); mutex_lock(&ha->tgt_mutex); spin_lock_irq(&pha->hardware_lock); @@ -5176,13 +5381,42 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, if (tgt->tgt_stop) goto send; - if (IS_FWI2_CAPABLE(ha)) { - atio7_entry_t *a = (atio7_entry_t *)atio; - sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id); - } else - sess = q2t_find_sess_by_loop_id(tgt, - GET_TARGET_ID(ha, (atio_entry_t *)atio)); + switch(prm->type) { + case Q2T_SESS_WORK_CMD: + { + struct q2t_cmd *cmd = prm->cmd; + if (IS_FWI2_CAPABLE(ha)) { + atio7_entry_t *a = (atio7_entry_t *)&cmd->atio; + s_id = a->fcp_hdr.s_id; + } else + loop_id = GET_TARGET_ID(ha, (atio_entry_t *)&cmd->atio); + break; + } + case Q2T_SESS_WORK_ABORT: + if (IS_FWI2_CAPABLE(ha)) { + sess = q2t_find_sess_by_s_id_le(tgt, + prm->abts.fcp_hdr_le.s_id); + goto after_find; + } else + loop_id = GET_TARGET_ID(ha, &prm->tm_iocb); + break; + case Q2T_SESS_WORK_TM: + if (IS_FWI2_CAPABLE(ha)) + s_id = prm->tm_iocb2.fcp_hdr.s_id; + else + loop_id = GET_TARGET_ID(ha, &prm->tm_iocb); + break; + default: + sBUG_ON(1); + break; + } + if (IS_FWI2_CAPABLE(ha)) + sess = q2t_find_sess_by_s_id(tgt, s_id); + else + sess = q2t_find_sess_by_loop_id(tgt, loop_id); + +after_find: if (sess != NULL) { TRACE_MGMT_DBG("sess %p found", sess); q2t_sess_get(sess); @@ -5192,37 +5426,117 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, * behind us. */ spin_unlock_irq(&pha->hardware_lock); - sess = q2t_make_local_sess(ha, atio); + sess = q2t_make_local_sess(ha, s_id, loop_id); spin_lock_irq(&pha->hardware_lock); /* sess has got an extra creation ref */ } send: - if (!tgt->tm_to_unknown && !tgt->tgt_stop && (sess != NULL)) { + if ((sess == NULL) || tgt->tgt_stop) + goto out_term; + + switch(prm->type) { + case Q2T_SESS_WORK_CMD: + { + struct q2t_cmd *cmd = prm->cmd; + if (tgt->tm_to_unknown) { + /* + * Cmd might be already aborted behind us, so be safe + * and abort it. It should be OK, initiator will retry + * it. + */ + goto out_term; + } TRACE_MGMT_DBG("Sending work cmd %p to SCST", cmd); - res = q2t_do_send_cmd_to_scst(ha, cmd, sess); - } else { - /* - * Cmd might be already aborted behind us, so be safe and - * abort it. It should be OK, initiator will retry it. It has - * not sent to SCST yet, so pass NULL as the second argument. - */ - TRACE_MGMT_DBG("Terminating work cmd %p", cmd); + rc = q2t_do_send_cmd_to_scst(ha, cmd, sess); + break; + } + case Q2T_SESS_WORK_ABORT: if (IS_FWI2_CAPABLE(ha)) - q24_send_term_exchange(ha, NULL , &cmd->atio.atio7, 1); + rc = __q24_handle_abts(ha, &prm->abts, sess); else - q2x_send_term_exchange(ha, NULL, &cmd->atio.atio2x, 1); - q2t_free_cmd(cmd); + rc = __q2t_abort_task(ha, &prm->tm_iocb, sess); + break; + case Q2T_SESS_WORK_TM: + { + uint8_t *lun; + uint16_t lun_data; + int lun_size, fn; + void *iocb; + + if (IS_FWI2_CAPABLE(ha)) { + atio7_entry_t *a = &prm->tm_iocb2; + iocb = a; + lun = (uint8_t *)&a->fcp_cmnd.lun; + lun_size = sizeof(a->fcp_cmnd.lun); + fn = a->fcp_cmnd.task_mgmt_flags; + } else { + notify_entry_t *n = &prm->tm_iocb; + iocb = n; + /* make it be in network byte order */ + lun_data = swab16(le16_to_cpu(n->lun)); + lun = (uint8_t *)&lun_data; + lun_size = sizeof(lun_data); + fn = n->task_flags >> IMM_NTFY_TASK_MGMT_SHIFT; + } + rc = q2t_issue_task_mgmt(sess, lun, lun_size, fn, iocb, 0); + break; + } + default: + sBUG_ON(1); + break; } + if (rc != 0) + goto out_term; + +out_put: if (sess != NULL) q2t_sess_put(sess); spin_unlock_irq(&pha->hardware_lock); mutex_unlock(&ha->tgt_mutex); - TRACE_EXIT_RES(res); - return res; + TRACE_EXIT(); + return; + +out_term: + switch(prm->type) { + case Q2T_SESS_WORK_CMD: + { + struct q2t_cmd *cmd = prm->cmd; + TRACE_MGMT_DBG("Terminating work cmd %p", cmd); + /* + * cmd has not sent to SCST yet, so pass NULL as the second + * argument + */ + if (IS_FWI2_CAPABLE(ha)) + q24_send_term_exchange(ha, NULL, &cmd->atio.atio7, 1); + else + q2x_send_term_exchange(ha, NULL, &cmd->atio.atio2x, 1); + q2t_free_cmd(cmd); + break; + } + case Q2T_SESS_WORK_ABORT: + if (IS_FWI2_CAPABLE(ha)) + q24_send_abts_resp(ha, &prm->abts, + SCST_MGMT_STATUS_REJECTED, false); + else + q2x_send_notify_ack(ha, &prm->tm_iocb, 0, + 0, 0, 0, 0, 0); + break; + case Q2T_SESS_WORK_TM: + if (IS_FWI2_CAPABLE(ha)) + q24_send_term_exchange(ha, NULL, &prm->tm_iocb2, 1); + else + q2x_send_notify_ack(ha, &prm->tm_iocb, 0, + 0, 0, 0, 0, 0); + break; + default: + sBUG_ON(1); + break; + } + goto out_put; } static void q2t_sess_work_fn(struct work_struct *work) @@ -5236,7 +5550,6 @@ static void q2t_sess_work_fn(struct work_struct *work) spin_lock_irq(&tgt->sess_work_lock); while (!list_empty(&tgt->sess_works_list)) { - int rc; struct q2t_sess_work_param *prm = list_entry( tgt->sess_works_list.next, typeof(*prm), sess_works_list_entry); @@ -5249,16 +5562,10 @@ static void q2t_sess_work_fn(struct work_struct *work) spin_unlock_irq(&tgt->sess_work_lock); - rc = q2t_exec_sess_work(tgt, prm); + q2t_exec_sess_work(tgt, prm); spin_lock_irq(&tgt->sess_work_lock); - if (rc != 0) { - TRACE_MGMT_DBG("Unable to complete sess work (tgt %p), " - "freeing cmd %p", tgt, prm->cmd); - q2t_free_cmd(prm->cmd); - } - kfree(prm); } spin_unlock_irq(&tgt->sess_work_lock); @@ -5374,6 +5681,7 @@ static int q2t_add_target(scsi_qla_host_t *ha) INIT_LIST_HEAD(&tgt->srr_ctio_list); INIT_LIST_HEAD(&tgt->srr_imm_list); INIT_WORK(&tgt->srr_work, q2t_handle_srr_work); + atomic_set(&tgt->tgt_global_resets_count, 0); ha->q2t_tgt = tgt; diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index e15eb1f48..f3aeb2adf 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -164,6 +164,8 @@ struct q2t_tgt { struct list_head srr_imm_list; struct work_struct srr_work; + atomic_t tgt_global_resets_count; + struct list_head tgt_list_entry; }; @@ -221,7 +223,18 @@ struct q2t_cmd { struct q2t_sess_work_param { struct list_head sess_works_list_entry; - struct q2t_cmd *cmd; + +#define Q2T_SESS_WORK_CMD 0 +#define Q2T_SESS_WORK_ABORT 1 +#define Q2T_SESS_WORK_TM 2 + int type; + + union { + struct q2t_cmd *cmd; + abts24_recv_entry_t abts; + notify_entry_t tm_iocb; + atio7_entry_t tm_iocb2; + }; }; struct q2t_mgmt_cmd { diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index 504f5632a..c48efe5fa 100644 --- a/qla2x00t/qla2x_tgt_def.h +++ b/qla2x00t/qla2x_tgt_def.h @@ -44,13 +44,13 @@ * Must be changed on any change in any initiator visible interfaces or * data in the target add-on */ -#define QLA2X_TARGET_MAGIC 269 +#define QLA2X_TARGET_MAGIC 270 /* * Must be changed on any change in any target visible interfaces or * data in the initiator */ -#define QLA2X_INITIATOR_MAGIC 57222 +#define QLA2X_INITIATOR_MAGIC 57223 #define QLA2X_INI_MODE_STR_EXCLUSIVE "exclusive" #define QLA2X_INI_MODE_STR_DISABLED "disabled" diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index a1693508d..6e97fe796 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -319,17 +319,6 @@ static DEVICE_ATTR(resource_counts, qla2x00_show_resource_counts, NULL); -typedef struct { - uint8_t port_name[WWN_SIZE]; - uint16_t loop_id; -} port_data_t; - -typedef struct { - uint8_t port_name[WWN_SIZE]; - uint16_t loop_id; - uint16_t reserved; -} port24_data_t; - static ssize_t qla2x00_show_port_database(struct device *dev, struct device_attribute *attr, char *buffer) @@ -339,48 +328,25 @@ qla2x00_show_port_database(struct device *dev, ulong size = 0; int rval, i; uint16_t entries; - mbx_cmd_t mc; - dma_addr_t pmap_dma; void *pmap; - ulong dma_size = 0x100 * sizeof(port24_data_t); - - pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size, - &pmap_dma, GFP_KERNEL); - if (pmap == NULL) { - size = scnprintf(buffer, max_size, "DMA Alloc failed of %ld", - dma_size); - 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(ha, &mc); + int pmap_len; + rval = qla2x00_get_node_name_list(ha, &pmap, &pmap_len); if (rval != QLA_SUCCESS) { size = scnprintf(buffer, max_size, - "Mailbox Command failed %d, mb0 %#x mb1 %#x\n", - rval, mc.mb[0], mc.mb[1]); - goto out_free; + "qla2x00_get_node_name_list() failed %d\n", + rval); + goto next; } size += scnprintf(buffer+size, max_size-size, - "Port Name List (%#04x) returned %d bytes\nL_ID WWPN\n", - MBC_PORT_NODE_NAME_LIST, le16_to_cpu(mc.mb[1])); + "Port Name List returned %d bytes\nL_ID WWPN\n", + pmap_len); if (IS_FWI2_CAPABLE(ha)) { - port24_data_t *pmap24 = pmap; + struct qla_port24_data *pmap24 = pmap; - entries = le16_to_cpu(mc.mb[1])/sizeof(*pmap24); + entries = pmap_len/sizeof(*pmap24); for (i = 0; (i < entries) && (size < max_size); ++i) { size += scnprintf(buffer+size, max_size-size, @@ -396,9 +362,9 @@ qla2x00_show_port_database(struct device *dev, pmap24[i].port_name[0]); } } else { - port_data_t *pmap2x = pmap; + struct qla_port23_data *pmap2x = pmap; - entries = le16_to_cpu(mc.mb[1])/sizeof(*pmap2x); + entries = pmap_len/sizeof(*pmap2x); for (i = 0; (i < entries) && (size < max_size); ++i) { size += scnprintf(buffer+size, max_size-size, @@ -415,9 +381,9 @@ qla2x00_show_port_database(struct device *dev, } } -out_free: - dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); + kfree(pmap); +next: if (size < max_size) { dma_addr_t gid_list_dma; struct gid_list_info *gid_list; diff --git a/qla2x00t/qla_fw.h b/qla2x00t/qla_fw.h index cf1945174..54289e530 100644 --- a/qla2x00t/qla_fw.h +++ b/qla2x00t/qla_fw.h @@ -26,6 +26,16 @@ #define PDO_FORCE_ADISC BIT_1 #define PDO_FORCE_PLOGI BIT_0 +struct qla_port23_data { + uint8_t port_name[WWN_SIZE]; + uint16_t loop_id; +}; + +struct qla_port24_data { + uint8_t port_name[WWN_SIZE]; + uint16_t loop_id; + uint16_t reserved; +}; #define PORT_DATABASE_24XX_SIZE 64 struct port_database_24xx { diff --git a/qla2x00t/qla_gbl.h b/qla2x00t/qla_gbl.h index b57497d22..25d86495c 100644 --- a/qla2x00t/qla_gbl.h +++ b/qla2x00t/qla_gbl.h @@ -183,6 +183,10 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *); extern int qla2x00_init_firmware(scsi_qla_host_t *, uint16_t); +extern int +qla2x00_get_node_name_list(scsi_qla_host_t *ha, + void **out_data, int *out_len); + extern int qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t); diff --git a/qla2x00t/qla_mbx.c b/qla2x00t/qla_mbx.c index fe6f73de3..a777af09d 100644 --- a/qla2x00t/qla_mbx.c +++ b/qla2x00t/qla_mbx.c @@ -1082,6 +1082,99 @@ out: 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 *ha, + void **out_data, int *out_len) +{ + int rval, left; + mbx_cmd_t mc; + dma_addr_t pmap_dma; + void *pmap; + struct qla_port24_data *list = NULL; + ulong dma_size; + + BUILD_BUG_ON(sizeof(struct qla_port24_data) < + sizeof(struct qla_port23_data)); + + 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 == NULL) { + printk(KERN_ERR "%s(%ld): DMA Alloc failed of " + "%ld\n", __func__, ha->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(ha, &mc); + if (rval != QLA_SUCCESS) { + if ((mc.mb[0] == MBS_COMMAND_ERROR) && + (mc.mb[1] == 0xA)) { + if (IS_FWI2_CAPABLE(ha)) + left += le16_to_cpu(mc.mb[2]) / sizeof(struct qla_port24_data); + else + left += le16_to_cpu(mc.mb[2]) / sizeof(struct qla_port23_data); + goto restart; + } + goto out_free; + } + + left = 0; + + list = kmalloc(dma_size, GFP_KERNEL); + if (list == NULL) { + printk(KERN_ERR "%s(%ld): failed to allocate node names" + " list structure.\n", __func__, ha->host_no); + rval = QLA_MEMORY_ALLOC_FAILED; + goto out_free; + } + + memcpy(list, pmap, dma_size); +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); + goto out; +} +EXPORT_SYMBOL(qla2x00_get_node_name_list); + /* * qla2x00_get_port_database * Issue normal/enhanced get port database mailbox command