summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorAlexei Potashnik <alexei@purestorage.com>2015-07-14 16:00:44 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2015-07-24 17:19:39 -0400
commita6ca88780dd66b0700d89419abd17b6b4bb49483 (patch)
tree4e0164cedd27e20cb356df6f3e9ec5b953823fd1 /drivers/scsi
parent8b2f5ff3d05c2c48b722c3cc67b8226f1601042b (diff)
qla2xxx: delay plogi/prli ack until existing sessions are deleted
- keep qla_tgt_sess object on the session list until it's freed - modify use of sess->deleted flag to differentiate delayed session deletion that can be cancelled from irreversible one: QLA_SESS_DELETION_PENDING vs QLA_SESS_DELETION_IN_PROGRESS - during IN_PROGRESS deletion all newly arrived commands and TMRs will be rejected, existing commands and TMRs will be terminated when given by the core to the fabric or simply dropped if session logout has already happened (logout terminates all existing exchanges) - new PLOGI will initiate deletion of the following sessions (unless deletion is already IN_PROGRESS): - with the same port_name (with logout) - different port_name, different loop_id but the same port_id (with logout) - different port_name, different port_id, but the same loop_id (without logout) - additionally each new PLOGI will store imm notify iocb in the same port_name session being deleted. When deletion process completes this iocb will be acked. Only the most recent PLOGI iocb is stored. The older ones will be terminated when replaced. - new PRLI will initiate deletion of the following sessions (unless deletion is already IN_PROGRESS): - different port_name, different port_id, but the same loop_id (without logout) Cc: <stable@vger.kernel.org> # v3.18+ Signed-off-by: Alexei Potashnik <alexei@purestorage.com> Acked-by: Quinn Tran <quinn.tran@qlogic.com> Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c438
-rw-r--r--drivers/scsi/qla2xxx/qla_target.h43
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c4
7 files changed, 481 insertions, 22 deletions
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index e63aa0780b52..05793b7199a4 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -67,10 +67,10 @@
67 * | | | 0xd031-0xd0ff | 67 * | | | 0xd031-0xd0ff |
68 * | | | 0xd101-0xd1fe | 68 * | | | 0xd101-0xd1fe |
69 * | | | 0xd214-0xd2fe | 69 * | | | 0xd214-0xd2fe |
70 * | Target Mode | 0xe079 | | 70 * | Target Mode | 0xe080 | |
71 * | Target Mode Management | 0xf083 | 0xf002 | 71 * | Target Mode Management | 0xf091 | 0xf002 |
72 * | | | 0xf046-0xf049 | 72 * | | | 0xf046-0xf049 |
73 * | Target Mode Task Management | 0x1000b | | 73 * | Target Mode Task Management | 0x1000d | |
74 * ---------------------------------------------------------------------- 74 * ----------------------------------------------------------------------
75 */ 75 */
76 76
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index d9c4ed21b52f..08a869f1ee03 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -274,6 +274,7 @@
274#define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/ 274#define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/
275 275
276struct req_que; 276struct req_que;
277struct qla_tgt_sess;
277 278
278/* 279/*
279 * (sd.h is not exported, hence local inclusion) 280 * (sd.h is not exported, hence local inclusion)
@@ -2026,6 +2027,7 @@ typedef struct fc_port {
2026 uint16_t port_id; 2027 uint16_t port_id;
2027 2028
2028 unsigned long retry_delay_timestamp; 2029 unsigned long retry_delay_timestamp;
2030 struct qla_tgt_sess *tgt_session;
2029} fc_port_t; 2031} fc_port_t;
2030 2032
2031#include "qla_mr.h" 2033#include "qla_mr.h"
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index e166c468a8b2..bda7a0d08b56 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -115,6 +115,8 @@ qla2x00_async_iocb_timeout(void *data)
115 QLA_LOGIO_LOGIN_RETRIED : 0; 115 QLA_LOGIO_LOGIN_RETRIED : 0;
116 qla2x00_post_async_login_done_work(fcport->vha, fcport, 116 qla2x00_post_async_login_done_work(fcport->vha, fcport,
117 lio->u.logio.data); 117 lio->u.logio.data);
118 } else if (sp->type == SRB_LOGOUT_CMD) {
119 qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT);
118 } 120 }
119} 121}
120 122
@@ -497,7 +499,10 @@ void
497qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, 499qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport,
498 uint16_t *data) 500 uint16_t *data)
499{ 501{
500 qla2x00_mark_device_lost(vha, fcport, 1, 0); 502 /* Don't re-login in target mode */
503 if (!fcport->tgt_session)
504 qla2x00_mark_device_lost(vha, fcport, 1, 0);
505 qlt_logo_completion_handler(fcport, data[0]);
501 return; 506 return;
502} 507}
503 508
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 36fbd4c7af8f..6f02b26a35cf 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -1943,6 +1943,9 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
1943 logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; 1943 logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
1944 logio->control_flags = 1944 logio->control_flags =
1945 cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); 1945 cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
1946 if (!sp->fcport->tgt_session ||
1947 !sp->fcport->tgt_session->keep_nport_handle)
1948 logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT);
1946 logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); 1949 logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
1947 logio->port_id[0] = sp->fcport->d_id.b.al_pa; 1950 logio->port_id[0] = sp->fcport->d_id.b.al_pa;
1948 logio->port_id[1] = sp->fcport->d_id.b.area; 1951 logio->port_id[1] = sp->fcport->d_id.b.area;
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index eca444827eae..acb2a50d3c55 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -114,6 +114,10 @@ static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha,
114 struct atio_from_isp *atio, uint16_t status, int qfull); 114 struct atio_from_isp *atio, uint16_t status, int qfull);
115static void qlt_disable_vha(struct scsi_qla_host *vha); 115static void qlt_disable_vha(struct scsi_qla_host *vha);
116static void qlt_clear_tgt_db(struct qla_tgt *tgt); 116static void qlt_clear_tgt_db(struct qla_tgt *tgt);
117static void qlt_send_notify_ack(struct scsi_qla_host *vha,
118 struct imm_ntfy_from_isp *ntfy,
119 uint32_t add_flags, uint16_t resp_code, int resp_code_valid,
120 uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan);
117/* 121/*
118 * Global Variables 122 * Global Variables
119 */ 123 */
@@ -382,14 +386,73 @@ static void qlt_free_session_done(struct work_struct *work)
382 struct qla_tgt *tgt = sess->tgt; 386 struct qla_tgt *tgt = sess->tgt;
383 struct scsi_qla_host *vha = sess->vha; 387 struct scsi_qla_host *vha = sess->vha;
384 struct qla_hw_data *ha = vha->hw; 388 struct qla_hw_data *ha = vha->hw;
389 unsigned long flags;
390 bool logout_started = false;
391 fc_port_t fcport;
392
393 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084,
394 "%s: se_sess %p / sess %p from port %8phC loop_id %#04x"
395 " s_id %02x:%02x:%02x logout %d keep %d plogi %d\n",
396 __func__, sess->se_sess, sess, sess->port_name, sess->loop_id,
397 sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
398 sess->logout_on_delete, sess->keep_nport_handle,
399 sess->plogi_ack_needed);
385 400
386 BUG_ON(!tgt); 401 BUG_ON(!tgt);
402
403 if (sess->logout_on_delete) {
404 int rc;
405
406 memset(&fcport, 0, sizeof(fcport));
407 fcport.loop_id = sess->loop_id;
408 fcport.d_id = sess->s_id;
409 memcpy(fcport.port_name, sess->port_name, WWN_SIZE);
410 fcport.vha = vha;
411 fcport.tgt_session = sess;
412
413 rc = qla2x00_post_async_logout_work(vha, &fcport, NULL);
414 if (rc != QLA_SUCCESS)
415 ql_log(ql_log_warn, vha, 0xf085,
416 "Schedule logo failed sess %p rc %d\n",
417 sess, rc);
418 else
419 logout_started = true;
420 }
421
387 /* 422 /*
388 * Release the target session for FC Nexus from fabric module code. 423 * Release the target session for FC Nexus from fabric module code.
389 */ 424 */
390 if (sess->se_sess != NULL) 425 if (sess->se_sess != NULL)
391 ha->tgt.tgt_ops->free_session(sess); 426 ha->tgt.tgt_ops->free_session(sess);
392 427
428 if (logout_started) {
429 bool traced = false;
430
431 while (!ACCESS_ONCE(sess->logout_completed)) {
432 if (!traced) {
433 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf086,
434 "%s: waiting for sess %p logout\n",
435 __func__, sess);
436 traced = true;
437 }
438 msleep(100);
439 }
440
441 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf087,
442 "%s: sess %p logout completed\n",
443 __func__, sess);
444 }
445
446 spin_lock_irqsave(&ha->hardware_lock, flags);
447
448 if (sess->plogi_ack_needed)
449 qlt_send_notify_ack(vha, &sess->tm_iocb,
450 0, 0, 0, 0, 0, 0);
451
452 list_del(&sess->sess_list_entry);
453
454 spin_unlock_irqrestore(&ha->hardware_lock, flags);
455
393 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, 456 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001,
394 "Unregistration of sess %p finished\n", sess); 457 "Unregistration of sess %p finished\n", sess);
395 458
@@ -410,9 +473,9 @@ void qlt_unreg_sess(struct qla_tgt_sess *sess)
410 473
411 vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); 474 vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess);
412 475
413 list_del(&sess->sess_list_entry); 476 if (!list_empty(&sess->del_list_entry))
414 if (sess->deleted) 477 list_del_init(&sess->del_list_entry);
415 list_del(&sess->del_list_entry); 478 sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
416 479
417 INIT_WORK(&sess->free_work, qlt_free_session_done); 480 INIT_WORK(&sess->free_work, qlt_free_session_done);
418 schedule_work(&sess->free_work); 481 schedule_work(&sess->free_work);
@@ -490,27 +553,36 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess,
490 struct qla_tgt *tgt = sess->tgt; 553 struct qla_tgt *tgt = sess->tgt;
491 uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5; 554 uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5;
492 555
493 if (sess->deleted) 556 if (sess->deleted) {
494 return; 557 /* Upgrade to unconditional deletion in case it was temporary */
558 if (immediate && sess->deleted == QLA_SESS_DELETION_PENDING)
559 list_del(&sess->del_list_entry);
560 else
561 return;
562 }
495 563
496 ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, 564 ql_dbg(ql_dbg_tgt, sess->vha, 0xe001,
497 "Scheduling sess %p for deletion\n", sess); 565 "Scheduling sess %p for deletion\n", sess);
498 list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
499 sess->deleted = 1;
500 566
501 if (immediate) 567 if (immediate) {
502 dev_loss_tmo = 0; 568 dev_loss_tmo = 0;
569 sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
570 list_add(&sess->del_list_entry, &tgt->del_sess_list);
571 } else {
572 sess->deleted = QLA_SESS_DELETION_PENDING;
573 list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
574 }
503 575
504 sess->expires = jiffies + dev_loss_tmo * HZ; 576 sess->expires = jiffies + dev_loss_tmo * HZ;
505 577
506 ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, 578 ql_dbg(ql_dbg_tgt, sess->vha, 0xe048,
507 "qla_target(%d): session for port %8phC (loop ID %d) scheduled for " 579 "qla_target(%d): session for port %8phC (loop ID %d) scheduled for "
508 "deletion in %u secs (expires: %lu) immed: %d\n", 580 "deletion in %u secs (expires: %lu) immed: %d, logout: %d\n",
509 sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo, 581 sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo,
510 sess->expires, immediate); 582 sess->expires, immediate, sess->logout_on_delete);
511 583
512 if (immediate) 584 if (immediate)
513 schedule_delayed_work(&tgt->sess_del_work, 0); 585 mod_delayed_work(system_wq, &tgt->sess_del_work, 0);
514 else 586 else
515 schedule_delayed_work(&tgt->sess_del_work, 587 schedule_delayed_work(&tgt->sess_del_work,
516 sess->expires - jiffies); 588 sess->expires - jiffies);
@@ -579,9 +651,9 @@ out_free_id_list:
579/* ha->hardware_lock supposed to be held on entry */ 651/* ha->hardware_lock supposed to be held on entry */
580static void qlt_undelete_sess(struct qla_tgt_sess *sess) 652static void qlt_undelete_sess(struct qla_tgt_sess *sess)
581{ 653{
582 BUG_ON(!sess->deleted); 654 BUG_ON(sess->deleted != QLA_SESS_DELETION_PENDING);
583 655
584 list_del(&sess->del_list_entry); 656 list_del_init(&sess->del_list_entry);
585 sess->deleted = 0; 657 sess->deleted = 0;
586} 658}
587 659
@@ -600,7 +672,9 @@ static void qlt_del_sess_work_fn(struct delayed_work *work)
600 del_list_entry); 672 del_list_entry);
601 elapsed = jiffies; 673 elapsed = jiffies;
602 if (time_after_eq(elapsed, sess->expires)) { 674 if (time_after_eq(elapsed, sess->expires)) {
603 qlt_undelete_sess(sess); 675 /* No turning back */
676 list_del_init(&sess->del_list_entry);
677 sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
604 678
605 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004, 679 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
606 "Timeout: sess %p about to be deleted\n", 680 "Timeout: sess %p about to be deleted\n",
@@ -644,6 +718,13 @@ static struct qla_tgt_sess *qlt_create_sess(
644 fcport->d_id.b.al_pa, fcport->d_id.b.area, 718 fcport->d_id.b.al_pa, fcport->d_id.b.area,
645 fcport->loop_id); 719 fcport->loop_id);
646 720
721 /* Cannot undelete at this point */
722 if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
723 spin_unlock_irqrestore(&ha->hardware_lock,
724 flags);
725 return NULL;
726 }
727
647 if (sess->deleted) 728 if (sess->deleted)
648 qlt_undelete_sess(sess); 729 qlt_undelete_sess(sess);
649 730
@@ -674,6 +755,14 @@ static struct qla_tgt_sess *qlt_create_sess(
674 sess->s_id = fcport->d_id; 755 sess->s_id = fcport->d_id;
675 sess->loop_id = fcport->loop_id; 756 sess->loop_id = fcport->loop_id;
676 sess->local = local; 757 sess->local = local;
758 INIT_LIST_HEAD(&sess->del_list_entry);
759
760 /* Under normal circumstances we want to logout from firmware when
761 * session eventually ends and release corresponding nport handle.
762 * In the exception cases (e.g. when new PLOGI is waiting) corresponding
763 * code will adjust these flags as necessary. */
764 sess->logout_on_delete = 1;
765 sess->keep_nport_handle = 0;
677 766
678 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, 767 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006,
679 "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", 768 "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n",
@@ -751,6 +840,10 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
751 mutex_unlock(&vha->vha_tgt.tgt_mutex); 840 mutex_unlock(&vha->vha_tgt.tgt_mutex);
752 841
753 spin_lock_irqsave(&ha->hardware_lock, flags); 842 spin_lock_irqsave(&ha->hardware_lock, flags);
843 } else if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
844 /* Point of no return */
845 spin_unlock_irqrestore(&ha->hardware_lock, flags);
846 return;
754 } else { 847 } else {
755 kref_get(&sess->se_sess->sess_kref); 848 kref_get(&sess->se_sess->sess_kref);
756 849
@@ -2371,6 +2464,19 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
2371 unsigned long flags = 0; 2464 unsigned long flags = 0;
2372 int res; 2465 int res;
2373 2466
2467 spin_lock_irqsave(&ha->hardware_lock, flags);
2468 if (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
2469 cmd->state = QLA_TGT_STATE_PROCESSED;
2470 if (cmd->sess->logout_completed)
2471 /* no need to terminate. FW already freed exchange. */
2472 qlt_abort_cmd_on_host_reset(cmd->vha, cmd);
2473 else
2474 qlt_send_term_exchange(vha, cmd, &cmd->atio, 1);
2475 spin_unlock_irqrestore(&ha->hardware_lock, flags);
2476 return 0;
2477 }
2478 spin_unlock_irqrestore(&ha->hardware_lock, flags);
2479
2374 memset(&prm, 0, sizeof(prm)); 2480 memset(&prm, 0, sizeof(prm));
2375 qlt_check_srr_debug(cmd, &xmit_type); 2481 qlt_check_srr_debug(cmd, &xmit_type);
2376 2482
@@ -2532,7 +2638,8 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
2532 2638
2533 spin_lock_irqsave(&ha->hardware_lock, flags); 2639 spin_lock_irqsave(&ha->hardware_lock, flags);
2534 2640
2535 if (qla2x00_reset_active(vha) || cmd->reset_count != ha->chip_reset) { 2641 if (qla2x00_reset_active(vha) || (cmd->reset_count != ha->chip_reset) ||
2642 (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS)) {
2536 /* 2643 /*
2537 * Either a chip reset is active or this request was from 2644 * Either a chip reset is active or this request was from
2538 * previous life, just abort the processing. 2645 * previous life, just abort the processing.
@@ -2725,6 +2832,89 @@ out:
2725 2832
2726/* If hardware_lock held on entry, might drop it, then reaquire */ 2833/* If hardware_lock held on entry, might drop it, then reaquire */
2727/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */ 2834/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
2835static int __qlt_send_term_imm_notif(struct scsi_qla_host *vha,
2836 struct imm_ntfy_from_isp *ntfy)
2837{
2838 struct nack_to_isp *nack;
2839 struct qla_hw_data *ha = vha->hw;
2840 request_t *pkt;
2841 int ret = 0;
2842
2843 ql_dbg(ql_dbg_tgt_tmr, vha, 0xe01c,
2844 "Sending TERM ELS CTIO (ha=%p)\n", ha);
2845
2846 pkt = (request_t *)qla2x00_alloc_iocbs_ready(vha, NULL);
2847 if (pkt == NULL) {
2848 ql_dbg(ql_dbg_tgt, vha, 0xe080,
2849 "qla_target(%d): %s failed: unable to allocate "
2850 "request packet\n", vha->vp_idx, __func__);
2851 return -ENOMEM;
2852 }
2853
2854 pkt->entry_type = NOTIFY_ACK_TYPE;
2855 pkt->entry_count = 1;
2856 pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
2857
2858 nack = (struct nack_to_isp *)pkt;
2859 nack->ox_id = ntfy->ox_id;
2860
2861 nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
2862 if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
2863 nack->u.isp24.flags = ntfy->u.isp24.flags &
2864 __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB);
2865 }
2866
2867 /* terminate */
2868 nack->u.isp24.flags |=
2869 __constant_cpu_to_le16(NOTIFY_ACK_FLAGS_TERMINATE);
2870
2871 nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
2872 nack->u.isp24.status = ntfy->u.isp24.status;
2873 nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
2874 nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle;
2875 nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
2876 nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
2877 nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
2878 nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
2879
2880 qla2x00_start_iocbs(vha, vha->req);
2881 return ret;
2882}
2883
2884static void qlt_send_term_imm_notif(struct scsi_qla_host *vha,
2885 struct imm_ntfy_from_isp *imm, int ha_locked)
2886{
2887 unsigned long flags = 0;
2888 int rc;
2889
2890 if (qlt_issue_marker(vha, ha_locked) < 0)
2891 return;
2892
2893 if (ha_locked) {
2894 rc = __qlt_send_term_imm_notif(vha, imm);
2895
2896#if 0 /* Todo */
2897 if (rc == -ENOMEM)
2898 qlt_alloc_qfull_cmd(vha, imm, 0, 0);
2899#endif
2900 goto done;
2901 }
2902
2903 spin_lock_irqsave(&vha->hw->hardware_lock, flags);
2904 rc = __qlt_send_term_imm_notif(vha, imm);
2905
2906#if 0 /* Todo */
2907 if (rc == -ENOMEM)
2908 qlt_alloc_qfull_cmd(vha, imm, 0, 0);
2909#endif
2910
2911done:
2912 if (!ha_locked)
2913 spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
2914}
2915
2916/* If hardware_lock held on entry, might drop it, then reaquire */
2917/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
2728static int __qlt_send_term_exchange(struct scsi_qla_host *vha, 2918static int __qlt_send_term_exchange(struct scsi_qla_host *vha,
2729 struct qla_tgt_cmd *cmd, 2919 struct qla_tgt_cmd *cmd,
2730 struct atio_from_isp *atio) 2920 struct atio_from_isp *atio)
@@ -3776,22 +3966,237 @@ static int qlt_abort_task(struct scsi_qla_host *vha,
3776 return __qlt_abort_task(vha, iocb, sess); 3966 return __qlt_abort_task(vha, iocb, sess);
3777} 3967}
3778 3968
3969void qlt_logo_completion_handler(fc_port_t *fcport, int rc)
3970{
3971 if (fcport->tgt_session) {
3972 if (rc != MBS_COMMAND_COMPLETE) {
3973 ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf088,
3974 "%s: se_sess %p / sess %p from"
3975 " port %8phC loop_id %#04x s_id %02x:%02x:%02x"
3976 " LOGO failed: %#x\n",
3977 __func__,
3978 fcport->tgt_session->se_sess,
3979 fcport->tgt_session,
3980 fcport->port_name, fcport->loop_id,
3981 fcport->d_id.b.domain, fcport->d_id.b.area,
3982 fcport->d_id.b.al_pa, rc);
3983 }
3984
3985 fcport->tgt_session->logout_completed = 1;
3986 }
3987}
3988
3989static void qlt_swap_imm_ntfy_iocb(struct imm_ntfy_from_isp *a,
3990 struct imm_ntfy_from_isp *b)
3991{
3992 struct imm_ntfy_from_isp tmp;
3993 memcpy(&tmp, a, sizeof(struct imm_ntfy_from_isp));
3994 memcpy(a, b, sizeof(struct imm_ntfy_from_isp));
3995 memcpy(b, &tmp, sizeof(struct imm_ntfy_from_isp));
3996}
3997
3998/*
3999* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list)
4000*
4001* Schedules sessions with matching port_id/loop_id but different wwn for
4002* deletion. Returns existing session with matching wwn if present.
4003* Null otherwise.
4004*/
4005static struct qla_tgt_sess *
4006qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn,
4007 port_id_t port_id, uint16_t loop_id)
4008{
4009 struct qla_tgt_sess *sess = NULL, *other_sess;
4010 uint64_t other_wwn;
4011
4012 list_for_each_entry(other_sess, &tgt->sess_list, sess_list_entry) {
4013
4014 other_wwn = wwn_to_u64(other_sess->port_name);
4015
4016 if (wwn == other_wwn) {
4017 WARN_ON(sess);
4018 sess = other_sess;
4019 continue;
4020 }
4021
4022 /* find other sess with nport_id collision */
4023 if (port_id.b24 == other_sess->s_id.b24) {
4024 if (loop_id != other_sess->loop_id) {
4025 ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000c,
4026 "Invalidating sess %p loop_id %d wwn %llx.\n",
4027 other_sess, other_sess->loop_id, other_wwn);
4028
4029 /*
4030 * logout_on_delete is set by default, but another
4031 * session that has the same s_id/loop_id combo
4032 * might have cleared it when requested this session
4033 * deletion, so don't touch it
4034 */
4035 qlt_schedule_sess_for_deletion(other_sess, true);
4036 } else {
4037 /*
4038 * Another wwn used to have our s_id/loop_id
4039 * combo - kill the session, but don't log out
4040 */
4041 sess->logout_on_delete = 0;
4042 qlt_schedule_sess_for_deletion(other_sess,
4043 true);
4044 }
4045 continue;
4046 }
4047
4048 /* find other sess with nport handle collision */
4049 if (loop_id == other_sess->loop_id) {
4050 ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000d,
4051 "Invalidating sess %p loop_id %d wwn %llx.\n",
4052 other_sess, other_sess->loop_id, other_wwn);
4053
4054 /* Same loop_id but different s_id
4055 * Ok to kill and logout */
4056 qlt_schedule_sess_for_deletion(other_sess, true);
4057 }
4058 }
4059
4060 return sess;
4061}
4062
3779/* 4063/*
3780 * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire 4064 * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
3781 */ 4065 */
3782static int qlt_24xx_handle_els(struct scsi_qla_host *vha, 4066static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
3783 struct imm_ntfy_from_isp *iocb) 4067 struct imm_ntfy_from_isp *iocb)
3784{ 4068{
4069 struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
4070 struct qla_tgt_sess *sess = NULL;
4071 uint64_t wwn;
4072 port_id_t port_id;
4073 uint16_t loop_id;
4074 uint16_t wd3_lo;
3785 int res = 0; 4075 int res = 0;
3786 4076
4077 wwn = wwn_to_u64(iocb->u.isp24.port_name);
4078
4079 port_id.b.domain = iocb->u.isp24.port_id[2];
4080 port_id.b.area = iocb->u.isp24.port_id[1];
4081 port_id.b.al_pa = iocb->u.isp24.port_id[0];
4082 port_id.b.rsvd_1 = 0;
4083
4084 loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
4085
3787 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, 4086 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026,
3788 "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n", 4087 "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n",
3789 vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode); 4088 vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode);
3790 4089
4090 /* res = 1 means ack at the end of thread
4091 * res = 0 means ack async/later.
4092 */
3791 switch (iocb->u.isp24.status_subcode) { 4093 switch (iocb->u.isp24.status_subcode) {
3792 case ELS_PLOGI: 4094 case ELS_PLOGI:
3793 case ELS_FLOGI: 4095
4096 if (wwn)
4097 sess = qlt_find_sess_invalidate_other(tgt, wwn,
4098 port_id, loop_id);
4099
4100 if (!sess || IS_SW_RESV_ADDR(sess->s_id)) {
4101 res = 1;
4102 break;
4103 }
4104
4105 if (sess->plogi_ack_needed) {
4106 /*
4107 * Initiator sent another PLOGI before last PLOGI could
4108 * finish. Swap plogi iocbs and terminate old one
4109 * without acking, new one will get acked when session
4110 * deletion completes.
4111 */
4112 ql_log(ql_log_warn, sess->vha, 0xf089,
4113 "sess %p received double plogi.\n", sess);
4114
4115 qlt_swap_imm_ntfy_iocb(iocb, &sess->tm_iocb);
4116
4117 qlt_send_term_imm_notif(vha, iocb, 1);
4118
4119 res = 0;
4120 break;
4121 }
4122
4123 res = 0;
4124
4125 /*
4126 * Save immediate Notif IOCB for Ack when sess is done
4127 * and being deleted.
4128 */
4129 memcpy(&sess->tm_iocb, iocb, sizeof(sess->tm_iocb));
4130 sess->plogi_ack_needed = 1;
4131
4132 /*
4133 * Under normal circumstances we want to release nport handle
4134 * during LOGO process to avoid nport handle leaks inside FW.
4135 * The exception is when LOGO is done while another PLOGI with
4136 * the same nport handle is waiting as might be the case here.
4137 * Note: there is always a possibily of a race where session
4138 * deletion has already started for other reasons (e.g. ACL
4139 * removal) and now PLOGI arrives:
4140 * 1. if PLOGI arrived in FW after nport handle has been freed,
4141 * FW must have assigned this PLOGI a new/same handle and we
4142 * can proceed ACK'ing it as usual when session deletion
4143 * completes.
4144 * 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT
4145 * bit reached it, the handle has now been released. We'll
4146 * get an error when we ACK this PLOGI. Nothing will be sent
4147 * back to initiator. Initiator should eventually retry
4148 * PLOGI and situation will correct itself.
4149 */
4150 sess->keep_nport_handle = ((sess->loop_id == loop_id) &&
4151 (sess->s_id.b24 == port_id.b24));
4152 qlt_schedule_sess_for_deletion(sess, true);
4153 break;
4154
3794 case ELS_PRLI: 4155 case ELS_PRLI:
4156 wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo);
4157
4158 if (wwn)
4159 sess = qlt_find_sess_invalidate_other(tgt, wwn, port_id,
4160 loop_id);
4161
4162 if (sess != NULL) {
4163 if (sess->deleted) {
4164 /*
4165 * Impatient initiator sent PRLI before last
4166 * PLOGI could finish. Will force him to re-try,
4167 * while last one finishes.
4168 */
4169 ql_log(ql_log_warn, sess->vha, 0xf090,
4170 "sess %p PRLI received, before plogi ack.\n",
4171 sess);
4172 qlt_send_term_imm_notif(vha, iocb, 1);
4173 res = 0;
4174 break;
4175 }
4176
4177 /*
4178 * This shouldn't happen under normal circumstances,
4179 * since we have deleted the old session during PLOGI
4180 */
4181 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf091,
4182 "PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n",
4183 sess->loop_id, sess, iocb->u.isp24.nport_handle);
4184
4185 sess->local = 0;
4186 sess->loop_id = loop_id;
4187 sess->s_id = port_id;
4188
4189 if (wd3_lo & BIT_7)
4190 sess->conf_compl_supported = 1;
4191
4192 res = 1;
4193 } else {
4194 /* todo: else - create sess here. */
4195 res = 1; /* send notify ack */
4196 }
4197
4198 break;
4199
3795 case ELS_LOGO: 4200 case ELS_LOGO:
3796 case ELS_PRLO: 4201 case ELS_PRLO:
3797 res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); 4202 res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
@@ -3809,6 +4214,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
3809 break; 4214 break;
3810 } 4215 }
3811 4216
4217 case ELS_FLOGI: /* should never happen */
3812 default: 4218 default:
3813 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061, 4219 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
3814 "qla_target(%d): Unsupported ELS command %x " 4220 "qla_target(%d): Unsupported ELS command %x "
diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h
index 310433504c0d..165efb5cc6c1 100644
--- a/drivers/scsi/qla2xxx/qla_target.h
+++ b/drivers/scsi/qla2xxx/qla_target.h
@@ -167,7 +167,24 @@ struct imm_ntfy_from_isp {
167 uint32_t srr_rel_offs; 167 uint32_t srr_rel_offs;
168 uint16_t srr_ui; 168 uint16_t srr_ui;
169 uint16_t srr_ox_id; 169 uint16_t srr_ox_id;
170 uint8_t reserved_4[19]; 170 union {
171 struct {
172 uint8_t node_name[8];
173 } plogi; /* PLOGI/ADISC/PDISC */
174 struct {
175 /* PRLI word 3 bit 0-15 */
176 uint16_t wd3_lo;
177 uint8_t resv0[6];
178 } prli;
179 struct {
180 uint8_t port_id[3];
181 uint8_t resv1;
182 uint16_t nport_handle;
183 uint16_t resv2;
184 } req_els;
185 } u;
186 uint8_t port_name[8];
187 uint8_t resv3[3];
171 uint8_t vp_index; 188 uint8_t vp_index;
172 uint32_t reserved_5; 189 uint32_t reserved_5;
173 uint8_t port_id[3]; 190 uint8_t port_id[3];
@@ -234,6 +251,7 @@ struct nack_to_isp {
234 uint8_t reserved[2]; 251 uint8_t reserved[2];
235 uint16_t ox_id; 252 uint16_t ox_id;
236} __packed; 253} __packed;
254#define NOTIFY_ACK_FLAGS_TERMINATE BIT_3
237#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0 255#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0
238#define NOTIFY_ACK_SRR_FLAGS_REJECT 1 256#define NOTIFY_ACK_SRR_FLAGS_REJECT 1
239 257
@@ -878,6 +896,13 @@ struct qla_tgt_sess_op {
878 bool aborted; 896 bool aborted;
879}; 897};
880 898
899enum qla_sess_deletion {
900 QLA_SESS_DELETION_NONE = 0,
901 QLA_SESS_DELETION_PENDING = 1, /* hopefully we can get rid of
902 * this one */
903 QLA_SESS_DELETION_IN_PROGRESS = 2,
904};
905
881/* 906/*
882 * Equivilant to IT Nexus (Initiator-Target) 907 * Equivilant to IT Nexus (Initiator-Target)
883 */ 908 */
@@ -886,8 +911,13 @@ struct qla_tgt_sess {
886 port_id_t s_id; 911 port_id_t s_id;
887 912
888 unsigned int conf_compl_supported:1; 913 unsigned int conf_compl_supported:1;
889 unsigned int deleted:1; 914 unsigned int deleted:2;
890 unsigned int local:1; 915 unsigned int local:1;
916 unsigned int logout_on_delete:1;
917 unsigned int plogi_ack_needed:1;
918 unsigned int keep_nport_handle:1;
919
920 unsigned char logout_completed;
891 921
892 struct se_session *se_sess; 922 struct se_session *se_sess;
893 struct scsi_qla_host *vha; 923 struct scsi_qla_host *vha;
@@ -899,6 +929,10 @@ struct qla_tgt_sess {
899 929
900 uint8_t port_name[WWN_SIZE]; 930 uint8_t port_name[WWN_SIZE];
901 struct work_struct free_work; 931 struct work_struct free_work;
932
933 union {
934 struct imm_ntfy_from_isp tm_iocb;
935 };
902}; 936};
903 937
904struct qla_tgt_cmd { 938struct qla_tgt_cmd {
@@ -1031,6 +1065,10 @@ struct qla_tgt_srr_ctio {
1031 struct qla_tgt_cmd *cmd; 1065 struct qla_tgt_cmd *cmd;
1032}; 1066};
1033 1067
1068/* Check for Switch reserved address */
1069#define IS_SW_RESV_ADDR(_s_id) \
1070 ((_s_id.b.domain == 0xff) && (_s_id.b.area == 0xfc))
1071
1034#define QLA_TGT_XMIT_DATA 1 1072#define QLA_TGT_XMIT_DATA 1
1035#define QLA_TGT_XMIT_STATUS 2 1073#define QLA_TGT_XMIT_STATUS 2
1036#define QLA_TGT_XMIT_ALL (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA) 1074#define QLA_TGT_XMIT_ALL (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA)
@@ -1124,5 +1162,6 @@ extern void qlt_stop_phase2(struct qla_tgt *);
1124extern irqreturn_t qla83xx_msix_atio_q(int, void *); 1162extern irqreturn_t qla83xx_msix_atio_q(int, void *);
1125extern void qlt_83xx_iospace_config(struct qla_hw_data *); 1163extern void qlt_83xx_iospace_config(struct qla_hw_data *);
1126extern int qlt_free_qfull_cmds(struct scsi_qla_host *); 1164extern int qlt_free_qfull_cmds(struct scsi_qla_host *);
1165extern void qlt_logo_completion_handler(fc_port_t *, int);
1127 1166
1128#endif /* __QLA_TARGET_H */ 1167#endif /* __QLA_TARGET_H */
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 9ad9b6996a6a..4e242c757947 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -1539,6 +1539,10 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id,
1539 } 1539 }
1540 1540
1541 sess->conf_compl_supported = conf_compl_supported; 1541 sess->conf_compl_supported = conf_compl_supported;
1542
1543 /* Reset logout parameters to default */
1544 sess->logout_on_delete = 1;
1545 sess->keep_nport_handle = 0;
1542} 1546}
1543 1547
1544/* 1548/*