3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/sysmacros.h>
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/scsi/scsi.h>
32 #include <sys/scsi/impl/scsi_reset_notify.h>
33 #include <sys/disp.h>
34 #include <sys/byteorder.h>
35 #include <sys/varargs.h>
36 #include <sys/atomic.h>
37 #include <sys/sdt.h>
38
39 #include <sys/stmf.h>
40 #include <sys/stmf_ioctl.h>
41 #include <sys/portif.h>
42 #include <sys/fct.h>
129
130 /*
131 * All solicited and unsolicited ELS will be handled here
132 */
133 if (iport->iport_rpwe_head) {
134 suggested_action |= fct_walk_discovery_queue(iport);
135 }
136
137 /*
138 * We only process it when there's no outstanding link init CMD
139 */
140 if ((iport->iport_link_state == PORT_STATE_LINK_INIT_START) &&
141 !(iport->iport_li_state & (LI_STATE_FLAG_CMD_WAITING |
142 LI_STATE_FLAG_NO_LI_YET))) {
143 suggested_action |= fct_process_link_init(iport);
144 }
145
146 /*
147 * We process cmd aborting in the end
148 */
149 if (iport->iport_abort_queue) {
150 suggested_action |= fct_cmd_terminator(iport);
151 }
152
153 /*
154 * Check cmd max/free
155 */
156 if (iport->iport_cmdcheck_clock <= ddi_get_lbolt()) {
157 suggested_action |= fct_check_cmdlist(iport);
158 iport->iport_cmdcheck_clock = ddi_get_lbolt() +
159 drv_usectohz(FCT_CMDLIST_CHECK_SECONDS * 1000000);
160 iport->iport_max_active_ncmds = 0;
161 }
162
163 if (iport->iport_offline_prstate != FCT_OPR_DONE) {
164 suggested_action |= fct_handle_port_offline(iport);
165 }
166
167 if (suggested_action & DISC_ACTION_RESCAN) {
168 continue;
169 } else if (suggested_action & DISC_ACTION_DELAY_RESCAN) {
604 ASSERT(IPORT_FLOGI_DONE(iport));
605 ASSERT(iport->iport_link_info.port_topology ==
606 PORT_TOPOLOGY_PT_TO_PT);
607 if (iport->iport_li_state & LI_STATE_FLAG_CMD_RETCHECK) {
608 iport->iport_li_state &= ~LI_STATE_FLAG_CMD_RETCHECK;
609 if (iport->iport_li_comp_status != FCT_SUCCESS) {
610 iport->iport_login_retry++;
611 if (iport->iport_login_retry >= 3) {
612 stmf_trace(iport->iport_alias, "Failing"
613 " to PLOGI to remote port in N2N "
614 " ret=%llx, forcing link down",
615 iport->iport_li_comp_status);
616 mutex_exit(&iport->iport_worker_lock);
617 fct_handle_event(iport->iport_port,
618 FCT_EVENT_LINK_DOWN, 0, 0);
619 mutex_enter(&iport->iport_worker_lock);
620 }
621 }
622 }
623 /* Find out if we need to do PLOGI at all */
624 if (iport->iport_nrps_login) {
625 iport->iport_li_state++;
626 atomic_and_32(&iport->iport_flags,
627 ~IPORT_ALLOW_UNSOL_FLOGI);
628 goto check_state_again;
629 }
630 if ((ddi_get_lbolt() >= iport->iport_li_cmd_timeout) &&
631 (!fct_lport_has_bigger_wwn(iport))) {
632 /* Cant wait forever */
633 stmf_trace(iport->iport_alias, "N2N: Remote port is "
634 "not logging in, forcing from our side");
635 force_login = 1;
636 } else {
637 force_login = 0;
638 }
639 if (force_login || fct_lport_has_bigger_wwn(iport)) {
640 elsop = ELS_OP_PLOGI;
641 wkdid = 1;
642 iport->iport_link_info.portid = 0xEF;
643 implicit = 0;
644 iport->iport_li_state |= LI_STATE_FLAG_CMD_RETCHECK;
645 } else {
646 ret = DISC_ACTION_DELAY_RESCAN;
647 }
648 break;
892 }
893 }
894
895 /*
896 * We always lookup by portid. port handles are too
897 * unreliable at this stage.
898 */
899 irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
900 if (els->els_req_payload[0] == ELS_OP_PLOGI) {
901 if (irp == NULL) {
902 /* drop the lock while we do allocations */
903 rw_exit(&iport->iport_lock);
904 rp = fct_alloc(FCT_STRUCT_REMOTE_PORT,
905 port->port_fca_rp_private_size, 0);
906 if (rp == NULL) {
907 fct_queue_cmd_for_termination(cmd,
908 FCT_ALLOC_FAILURE);
909 return;
910 }
911 irp = (fct_i_remote_port_t *)rp->rp_fct_private;
912 rw_init(&irp->irp_lock, 0, RW_DRIVER, 0);
913 irp->irp_rp = rp;
914 irp->irp_portid = cmd->cmd_rportid;
915 rp->rp_port = port;
916 rp->rp_id = cmd->cmd_rportid;
917 rp->rp_handle = FCT_HANDLE_NONE;
918 /*
919 * Grab port lock as writer since we are going
920 * to modify the local port struct.
921 */
922 rw_enter(&iport->iport_lock, RW_WRITER);
923 /* Make sure nobody created the struct except us */
924 if (fct_portid_to_portptr(iport, cmd->cmd_rportid)) {
925 /* Oh well, free it */
926 fct_free(rp);
927 } else {
928 fct_queue_rp(iport, irp);
929 }
930 rw_downgrade(&iport->iport_lock);
931 /* Start over becasue we dropped the lock */
970 cmd_slot = fct_alloc_cmd_slot(iport, cmd);
971 if (cmd_slot == FCT_SLOT_EOL) {
972 /* This should not have happened */
973 rw_exit(&iport->iport_lock);
974 stmf_trace(iport->iport_alias,
975 "ran out of xchg resources");
976 fct_queue_cmd_for_termination(cmd,
977 FCT_NO_XCHG_RESOURCE);
978 return;
979 }
980 } else {
981 /*
982 * Tell the framework that fct_cmd_free() can decrement the
983 * irp_nonfcp_xchg_count variable.
984 */
985 atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
986 }
987 atomic_inc_16(&irp->irp_nonfcp_xchg_count);
988
989 /*
990 * Grab the remote port lock while we modify the port state.
991 * we should not drop the fca port lock (as a reader) until we
992 * modify the remote port state.
993 */
994 rw_enter(&irp->irp_lock, RW_WRITER);
995 if ((op == ELS_OP_PLOGI) || (op == ELS_OP_PRLI) ||
996 (op == ELS_OP_LOGO) || (op == ELS_OP_PRLO) ||
997 (op == ELS_OP_TPRLO)) {
998 uint32_t rf = IRP_PRLI_DONE;
999 if ((op == ELS_OP_PLOGI) || (op == ELS_OP_LOGO)) {
1000 rf |= IRP_PLOGI_DONE;
1001 if (irp->irp_flags & IRP_PLOGI_DONE)
1002 atomic_dec_32(&iport->iport_nrps_login);
1003 }
1004 atomic_inc_16(&irp->irp_sa_elses_count);
1005 atomic_and_32(&irp->irp_flags, ~rf);
1006 atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
1007 } else {
1008 atomic_inc_16(&irp->irp_nsa_elses_count);
1009 }
1040 for (i = 0; i < port->port_max_xchges; i++) {
1041 if (iport->iport_cmd_slots[i].slot_cmd == NULL)
1042 continue;
1043 icmd = iport->iport_cmd_slots[i].slot_cmd;
1044 if (icmd->icmd_flags & ICMD_IN_TRANSITION) {
1045 unhandled++;
1046 continue;
1047 }
1048
1049 if (icmd->icmd_flags & ICMD_CMD_COMPLETE) {
1050 unhandled++;
1051 continue;
1052 }
1053
1054 cmd = icmd->icmd_cmd;
1055 if (cmd->cmd_rp != rp) {
1056 skipped++;
1057 continue;
1058 }
1059 if (cmd->cmd_type & ttc) {
1060 if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
1061 fct_queue_scsi_task_for_termination(cmd,
1062 FCT_ABORTED);
1063 else
1064 fct_q_for_termination_lock_held(iport, icmd,
1065 FCT_ABORTED);
1066 cleaned++;
1067 } else {
1068 skipped++;
1069 }
1070 }
1071 if (((cleaned + skipped) == total) && (unhandled == 0)) {
1072 ret = 1;
1073 } else {
1074 /*
1075 * XXX: handle this situation.
1076 */
1077 stmf_trace(iport->iport_alias, "Clean up trouble for irp"
1078 " %p, c/s/u/t = %d/%d/%d/%d", irp, cleaned, skipped,
1079 unhandled, total);
1080 ret = 0;
1081 }
1082 if ((cleaned) && IS_WORKER_SLEEPING(iport))
1083 cv_signal(&iport->iport_worker_cv);
1084 mutex_exit(&iport->iport_worker_lock);
1085 rw_exit(&irp->irp_lock);
1086 rw_exit(&iport->iport_lock);
1087 return (ret);
1088 }
1089
1090 void
1091 fct_dequeue_els(fct_i_remote_port_t *irp)
1092 {
1093 fct_i_cmd_t *icmd;
1094
1095 rw_enter(&irp->irp_lock, RW_WRITER);
1096 icmd = irp->irp_els_list;
1097 irp->irp_els_list = icmd->icmd_next;
1098 atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_IRP_QUEUE);
1099 rw_exit(&irp->irp_lock);
1100 }
1101
1102 fct_status_t
1103 fct_register_remote_port(fct_local_port_t *port, fct_remote_port_t *rp,
1104 fct_cmd_t *cmd)
1105 {
1106 fct_status_t ret;
1107 fct_i_local_port_t *iport;
1108 fct_i_remote_port_t *irp;
1109 int i;
1110 char info[FCT_INFO_LEN];
1111
1112 iport = (fct_i_local_port_t *)port->port_fct_private;
1113 irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1114
1115 if ((ret = port->port_register_remote_port(port, rp, cmd)) !=
1116 FCT_SUCCESS)
1117 return (ret);
1172
1173 hba_fatal_err:;
1174 rw_exit(&irp->irp_lock);
1175 rw_exit(&iport->iport_lock);
1176 /*
1177 * XXX Throw HBA fatal error event
1178 */
1179 (void) fct_port_shutdown(iport->iport_port,
1180 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1181 return (FCT_FAILURE);
1182 }
1183
1184 fct_status_t
1185 fct_deregister_remote_port(fct_local_port_t *port, fct_remote_port_t *rp)
1186 {
1187 fct_status_t ret = FCT_SUCCESS;
1188 fct_i_local_port_t *iport = PORT_TO_IPORT(port);
1189 fct_i_remote_port_t *irp = RP_TO_IRP(rp);
1190
1191 if (irp->irp_snn) {
1192 kmem_free(irp->irp_snn, strlen(irp->irp_snn) + 1);
1193 irp->irp_snn = NULL;
1194 }
1195 if (irp->irp_spn) {
1196 kmem_free(irp->irp_spn, strlen(irp->irp_spn) + 1);
1197 irp->irp_spn = NULL;
1198 }
1199
1200 if ((ret = port->port_deregister_remote_port(port, rp)) !=
1201 FCT_SUCCESS) {
1202 return (ret);
1203 }
1204
1205 if (irp->irp_flags & IRP_HANDLE_OPENED) {
1206 atomic_and_32(&irp->irp_flags, ~IRP_HANDLE_OPENED);
1207 iport->iport_rp_slots[rp->rp_handle] = NULL;
1208 }
1209 atomic_inc_64(&iport->iport_last_change);
1210 fct_log_remote_port_event(port, ESC_SUNFC_TARGET_REMOVE,
1211 rp->rp_pwwn, rp->rp_id);
1212
1213 return (FCT_SUCCESS);
1214 }
1215
1216 fct_status_t
1217 fct_send_accrjt(fct_cmd_t *cmd, uint8_t accrjt, uint8_t reason, uint8_t expl)
1232 return (port->port_send_cmd_response(cmd, 0));
1233 }
1234
1235
1236 disc_action_t
1237 fct_walk_discovery_queue(fct_i_local_port_t *iport)
1238 {
1239 char info[FCT_INFO_LEN];
1240 fct_i_remote_port_t **pirp;
1241 fct_i_remote_port_t *prev_irp = NULL;
1242 disc_action_t suggested_action = DISC_ACTION_NO_WORK;
1243 fct_i_remote_port_t *irp_dereg_list = NULL;
1244 fct_i_remote_port_t *irp_cur_item = NULL;
1245
1246 for (pirp = &iport->iport_rpwe_head; *pirp != NULL; ) {
1247 fct_i_remote_port_t *irp = *pirp;
1248 disc_action_t ret = DISC_ACTION_NO_WORK;
1249 int do_deregister = 0;
1250 int irp_deregister_timer = 0;
1251
1252 if (irp->irp_els_list) {
1253 ret |= fct_process_els(iport, irp);
1254 }
1255
1256 irp_deregister_timer = irp->irp_deregister_timer;
1257 if (irp_deregister_timer) {
1258 if (ddi_get_lbolt() >= irp_deregister_timer) {
1259 do_deregister = 1;
1260 } else {
1261 ret |= DISC_ACTION_DELAY_RESCAN;
1262 }
1263 }
1264 suggested_action |= ret;
1265
1266 if (irp->irp_els_list == NULL) {
1267 mutex_exit(&iport->iport_worker_lock);
1268 rw_enter(&iport->iport_lock, RW_WRITER);
1269 rw_enter(&irp->irp_lock, RW_WRITER);
1270 mutex_enter(&iport->iport_worker_lock);
1271 if (irp->irp_els_list == NULL) {
1272 if (!irp_deregister_timer ||
1273 (do_deregister &&
1274 !irp->irp_sa_elses_count &&
1275 !irp->irp_nsa_elses_count &&
1276 !irp->irp_fcp_xchg_count &&
1277 !irp->irp_nonfcp_xchg_count)) {
1278 /* dequeue irp from discovery queue */
1279 atomic_and_32(&irp->irp_flags,
1280 ~IRP_IN_DISCOVERY_QUEUE);
1281 *pirp = irp->irp_discovery_next;
1282 if (iport->iport_rpwe_head == NULL)
1283 iport->iport_rpwe_tail = NULL;
1284 else if (irp == iport->iport_rpwe_tail)
1285 iport->iport_rpwe_tail =
1286 prev_irp;
1287
1288 irp->irp_discovery_next = NULL;
1289 if (do_deregister) {
1290 fct_deque_rp(iport, irp);
1291 rw_exit(&irp->irp_lock);
1499 * a response to PLOGI. Because the initiator
1500 * will send a PRLI as soon as it responds to PLOGI.
1501 * Check fct_process_els() for more info.
1502 */
1503 atomic_or_32(&irp->irp_flags,
1504 IRP_SOL_PLOGI_IN_PROGRESS);
1505 atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
1506 ret = port->port_send_cmd(cmd);
1507 if (ret != FCT_SUCCESS) {
1508 atomic_and_32(&icmd->icmd_flags,
1509 ~ICMD_KNOWN_TO_FCA);
1510 atomic_and_32(&irp->irp_flags,
1511 ~IRP_SOL_PLOGI_IN_PROGRESS);
1512 }
1513 }
1514 }
1515 atomic_dec_16(&irp->irp_sa_elses_count);
1516
1517 if (ret == FCT_SUCCESS) {
1518 if (cmd_type == FCT_CMD_RCVD_ELS) {
1519 atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
1520 atomic_inc_32(&iport->iport_nrps_login);
1521 if (irp->irp_deregister_timer)
1522 irp->irp_deregister_timer = 0;
1523 }
1524 if (icmd_flags & ICMD_IMPLICIT) {
1525 DTRACE_FC_5(rport__login__end,
1526 fct_cmd_t, cmd,
1527 fct_local_port_t, port,
1528 fct_i_remote_port_t, irp,
1529 int, (cmd_type != FCT_CMD_RCVD_ELS),
1530 int, FCT_SUCCESS);
1531
1532 p = els->els_resp_payload;
1533 p[0] = ELS_OP_ACC;
1534 cmd->cmd_comp_status = FCT_SUCCESS;
1535 fct_send_cmd_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE);
1536 }
1537 } else {
1538 DTRACE_FC_5(rport__login__end,
1539 fct_cmd_t, cmd,
1540 fct_local_port_t, port,
1541 fct_i_remote_port_t, irp,
1542 int, (cmd_type != FCT_CMD_RCVD_ELS),
1985 if (ret != FCT_SUCCESS) {
1986 fct_queue_cmd_for_termination(icmd->icmd_cmd, ret);
1987 } else {
1988 if (fct_rscn_options & RSCN_OPTION_VERIFY) {
1989 fct_rscn_verify(iport, rscn_req_payload,
1990 rscn_req_size);
1991 }
1992 }
1993
1994 kmem_free(rscn_req_payload, rscn_req_size);
1995 } else {
1996 ASSERT(0);
1997 }
1998
1999 return (DISC_ACTION_RESCAN);
2000 }
2001
2002 disc_action_t
2003 fct_process_els(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
2004 {
2005 fct_i_cmd_t *cmd_to_abort = NULL;
2006 fct_i_cmd_t **ppcmd, *icmd;
2007 fct_cmd_t *cmd;
2008 fct_els_t *els;
2009 int dq;
2010 disc_action_t ret = DISC_ACTION_NO_WORK;
2011 uint8_t op;
2012
2013 mutex_exit(&iport->iport_worker_lock);
2014
2015 /*
2016 * Do some cleanup based on the following.
2017 * - We can only have one session affecting els pending.
2018 * - If any session affecting els is pending no other els is allowed.
2019 * - If PLOGI is not done, nothing except PLOGI or LOGO is allowed.
2020 * NOTE: If port is down the cleanup is done outside of this
2021 * function.
2022 * NOTE: There is a side effect, if a sa ELS (non PLOGI) is received
2023 * while a PLOGI is pending, it will kill itself and the PLOGI.
2024 * which is probably ok.
2025 */
2026 rw_enter(&irp->irp_lock, RW_WRITER);
2027 ppcmd = &irp->irp_els_list;
2028 while ((*ppcmd) != NULL) {
2029 int special_prli_cond = 0;
2030 dq = 0;
2031
2032 els = (fct_els_t *)((*ppcmd)->icmd_cmd)->cmd_specific;
2033
2034 if (((*ppcmd)->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS) &&
2035 (els->els_req_payload[0] == ELS_OP_PRLI) &&
2036 (irp->irp_flags & IRP_SOL_PLOGI_IN_PROGRESS)) {
2037 /*
2038 * The initiator sent a PRLI right after responding
2039 * to PLOGI and we have not yet finished processing
2040 * the PLOGI completion. We should not kill the PRLI
2041 * as the initiator may not retry it.
2042 */
2043 special_prli_cond = 1;
2044 }
2045
2046 if ((*ppcmd)->icmd_flags & ICMD_BEING_ABORTED) {
2047 dq = 1;
2048 } else if (irp->irp_sa_elses_count > 1) {
2049 dq = 1;
2050 /* This els might have set the CLEANUP flag */
2051 atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
2052 stmf_trace(iport->iport_alias, "Killing ELS %x cond 1",
2053 els->els_req_payload[0]);
2054 } else if (irp->irp_sa_elses_count &&
2055 (((*ppcmd)->icmd_flags & ICMD_SESSION_AFFECTING) == 0)) {
2056 stmf_trace(iport->iport_alias, "Killing ELS %x cond 2",
2057 els->els_req_payload[0]);
2058 dq = 1;
2059 } else if (((irp->irp_flags & IRP_PLOGI_DONE) == 0) &&
2060 (els->els_req_payload[0] != ELS_OP_PLOGI) &&
2061 (els->els_req_payload[0] != ELS_OP_LOGO) &&
2062 (special_prli_cond == 0)) {
2063 stmf_trace(iport->iport_alias, "Killing ELS %x cond 3",
2064 els->els_req_payload[0]);
2065 dq = 1;
2066 }
2067
2068 if (dq) {
2069 fct_i_cmd_t *c = (*ppcmd)->icmd_next;
2070
2071 if ((*ppcmd)->icmd_flags & ICMD_SESSION_AFFECTING)
2072 atomic_dec_16(&irp->irp_sa_elses_count);
2073 else
2074 atomic_dec_16(&irp->irp_nsa_elses_count);
2075 (*ppcmd)->icmd_next = cmd_to_abort;
2076 cmd_to_abort = *ppcmd;
2077 *ppcmd = c;
2078 } else {
2079 ppcmd = &((*ppcmd)->icmd_next);
2080 }
2081 }
2082 rw_exit(&irp->irp_lock);
2083
2084 while (cmd_to_abort) {
2085 fct_i_cmd_t *c = cmd_to_abort->icmd_next;
2086
2087 atomic_and_32(&cmd_to_abort->icmd_flags, ~ICMD_IN_IRP_QUEUE);
2088 fct_queue_cmd_for_termination(cmd_to_abort->icmd_cmd,
2089 FCT_ABORTED);
2090 cmd_to_abort = c;
2091 }
2092
2093 /*
2094 * pick from the top of the queue
2095 */
2096 icmd = irp->irp_els_list;
2097 if (icmd == NULL) {
2098 /*
2099 * The cleanup took care of everything.
2100 */
2101
2102 mutex_enter(&iport->iport_worker_lock);
2103 return (DISC_ACTION_RESCAN);
2104 }
2105
2106 cmd = icmd->icmd_cmd;
2107 els = ICMD_TO_ELS(icmd);
2108 op = els->els_req_payload[0];
2109 if ((icmd->icmd_flags & ICMD_ELS_PROCESSING_STARTED) == 0) {
2110 stmf_trace(iport->iport_alias, "Processing %ssol ELS %x (%s) "
2111 "rp_id=%x", (cmd->cmd_type == FCT_CMD_RCVD_ELS) ? "un" : "",
2112 op, FCT_ELS_NAME(op), cmd->cmd_rportid);
2113 atomic_or_32(&icmd->icmd_flags, ICMD_ELS_PROCESSING_STARTED);
2114 }
2115
2116 if (op == ELS_OP_PLOGI) {
2162 }
2163
2164 void
2165 fct_handle_sol_els_completion(fct_i_local_port_t *iport, fct_i_cmd_t *icmd)
2166 {
2167 fct_i_remote_port_t *irp = NULL;
2168 fct_els_t *els = ICMD_TO_ELS(icmd);
2169 uint8_t op = els->els_req_payload[0];
2170
2171 if (icmd->icmd_cmd->cmd_rp) {
2172 irp = ICMD_TO_IRP(icmd);
2173 }
2174 if (icmd->icmd_cmd->cmd_rp &&
2175 (icmd->icmd_cmd->cmd_comp_status == FCT_SUCCESS) &&
2176 (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2177 bcopy(els->els_resp_payload + 20, irp->irp_rp->rp_pwwn, 8);
2178 bcopy(els->els_resp_payload + 28, irp->irp_rp->rp_nwwn, 8);
2179
2180 stmf_wwn_to_devid_desc((scsi_devid_desc_t *)irp->irp_id,
2181 irp->irp_rp->rp_pwwn, PROTOCOL_FIBRE_CHANNEL);
2182 atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
2183 atomic_inc_32(&iport->iport_nrps_login);
2184 if (irp->irp_deregister_timer) {
2185 irp->irp_deregister_timer = 0;
2186 irp->irp_dereg_count = 0;
2187 }
2188 }
2189
2190 if (irp && (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2191 atomic_and_32(&irp->irp_flags, ~IRP_SOL_PLOGI_IN_PROGRESS);
2192 }
2193 atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2194 stmf_trace(iport->iport_alias, "Sol ELS %x (%s) completed with "
2195 "status %llx, did/%x", op, FCT_ELS_NAME(op),
2196 icmd->icmd_cmd->cmd_comp_status, icmd->icmd_cmd->cmd_rportid);
2197 }
2198
2199 static disc_action_t
2200 fct_check_cmdlist(fct_i_local_port_t *iport)
2201 {
2202 int num_to_release, ndx;
2203 fct_i_cmd_t *icmd;
2204 uint32_t total, max_active;
2205
2206 ASSERT(MUTEX_HELD(&iport->iport_worker_lock));
2207
2208 total = iport->iport_total_alloced_ncmds;
2209 max_active = iport->iport_max_active_ncmds;
2210
2211 if (total <= max_active)
2212 return (DISC_ACTION_NO_WORK);
2213 /*
2214 * Everytime, we release half of the difference
2215 */
2216 num_to_release = (total + 1 - max_active) / 2;
2217
2218 mutex_exit(&iport->iport_worker_lock);
2219 for (ndx = 0; ndx < num_to_release; ndx++) {
2220 mutex_enter(&iport->iport_cached_cmd_lock);
2221 icmd = iport->iport_cached_cmdlist;
2222 if (icmd == NULL) {
2223 mutex_exit(&iport->iport_cached_cmd_lock);
2224 break;
2225 }
2226 iport->iport_cached_cmdlist = icmd->icmd_next;
2227 iport->iport_cached_ncmds--;
2228 mutex_exit(&iport->iport_cached_cmd_lock);
2229 atomic_dec_32(&iport->iport_total_alloced_ncmds);
2230 fct_free(icmd->icmd_cmd);
2231 }
2232 mutex_enter(&iport->iport_worker_lock);
2233 return (DISC_ACTION_RESCAN);
2234 }
2235
2236 /*
2237 * The efficiency of handling solicited commands is very low here. But
2238 * fortunately, we seldom send solicited commands. So it will not hurt
2239 * the system performance much.
2240 */
2241 static disc_action_t
2242 fct_check_solcmd_queue(fct_i_local_port_t *iport)
2243 {
2244 fct_i_cmd_t *icmd = NULL;
2245 fct_i_cmd_t *prev_icmd = NULL;
2246 fct_i_cmd_t *next_icmd = NULL;
2275 * mutex ???
2276 */
2277 icmd->icmd_cb(icmd);
2278 }
2279
2280
2281 /*
2282 * Release resources for this solicited cmd
2283 */
2284 if (iport->iport_solcmd_queue == icmd) {
2285 iport->iport_solcmd_queue = next_icmd;
2286 } else {
2287 prev_icmd = iport->iport_solcmd_queue;
2288 while (prev_icmd->icmd_solcmd_next != icmd) {
2289 prev_icmd = prev_icmd->icmd_solcmd_next;
2290 }
2291 prev_icmd->icmd_solcmd_next = next_icmd;
2292 }
2293
2294 icmd->icmd_cb = NULL;
2295 mutex_exit(&iport->iport_worker_lock);
2296 fct_cmd_free(icmd->icmd_cmd);
2297 mutex_enter(&iport->iport_worker_lock);
2298 } else {
2299 /*
2300 * This solicited cmd is still ongoing.
2301 * We need check if it's time to abort this cmd
2302 */
2303 if (((icmd->icmd_start_time + drv_usectohz(
2304 USEC_SOL_TIMEOUT)) < ddi_get_lbolt()) &&
2305 !(icmd->icmd_flags & ICMD_BEING_ABORTED)) {
2306 fct_q_for_termination_lock_held(iport,
2307 icmd, FCT_ABORTED);
2308 }
2309 }
2310 }
2311
2312 return (DISC_ACTION_DELAY_RESCAN);
2313 }
2314
2411
2412 rw_enter(&ICMD_TO_IPORT(icmd)->iport_lock, RW_READER);
2413 mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2414 query_irp = fct_lookup_irp_by_nodewwn(ICMD_TO_IPORT(icmd),
2415 ICMD_TO_CT(icmd)->ct_req_payload + 16);
2416
2417 if (!query_irp) {
2418 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2419 "can't get rp icmd-%p", icmd);
2420 goto exit_gsnn_cb;
2421 } else {
2422 snlen = ICMD_TO_CT(icmd)->ct_resp_payload[16];
2423 }
2424
2425 if (query_irp && snlen) {
2426 /*
2427 * Release previous resource, then allocate needed resource
2428 */
2429 sn = query_irp->irp_snn;
2430 if (sn) {
2431 kmem_free(sn, strlen(sn) + 1);
2432 }
2433
2434 query_irp->irp_snn = NULL;
2435 sn = kmem_zalloc(snlen + 1, KM_SLEEP);
2436 (void) strncpy(sn, (char *)
2437 ICMD_TO_CT(icmd)->ct_resp_payload + 17, snlen);
2438 if (strlen(sn) != snlen) {
2439 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias,
2440 "fct_gsnn_cb: %s, but len=%d", sn, snlen);
2441 kmem_free(sn, snlen + 1);
2442 sn = NULL;
2443 }
2444
2445 /*
2446 * Update symbolic node name
2447 */
2448 query_irp->irp_snn = sn;
2449 if ((query_irp->irp_flags & IRP_SCSI_SESSION_STARTED) &&
2450 (query_irp->irp_session)) {
2451 query_irp->irp_session->ss_rport_alias =
2452 query_irp->irp_snn;
2453 }
2454 } else {
2455 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2456 "irp/%p, snlen/%d", query_irp, snlen);
2457 }
2458
2459 exit_gsnn_cb:
2460 rw_exit(&ICMD_TO_IPORT(icmd)->iport_lock);
2461 }
2462
2463 void
2464 fct_link_init_cb(fct_i_cmd_t *icmd)
2465 {
2466 fct_i_local_port_t *iport = ICMD_TO_IPORT(icmd);
2467
2468 iport->iport_li_state &= ~LI_STATE_FLAG_CMD_WAITING;
2639
2640 if (!FCT_IS_CT_ACC(icmd)) {
2641 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gspn_cb: "
2642 "GSPN_ID is not accepted by NS - icmd/%p", icmd);
2643 return;
2644 }
2645 mutex_exit(&iport->iport_worker_lock);
2646
2647 resp = ct->ct_resp_payload;
2648 req = ct->ct_req_payload;
2649 query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2650
2651 rw_enter(&iport->iport_lock, RW_READER);
2652 mutex_enter(&iport->iport_worker_lock);
2653 query_irp = fct_portid_to_portptr(iport, query_portid);
2654 if (query_irp) {
2655 spnlen = resp[16];
2656 if (spnlen > 0) {
2657 if (query_irp->irp_spn) {
2658 kmem_free(query_irp->irp_spn,
2659 strlen(query_irp->irp_spn) + 1);
2660 }
2661 query_irp->irp_spn = kmem_zalloc(spnlen + 1, KM_SLEEP);
2662 (void) strncpy(query_irp->irp_spn,
2663 (char *)resp + 17, spnlen);
2664 }
2665 }
2666 rw_exit(&iport->iport_lock);
2667 }
2668
2669 void
2670 fct_rls_cb(fct_i_cmd_t *icmd)
2671 {
2672 fct_els_t *els = ICMD_TO_ELS(icmd);
2673 uint8_t *resp;
2674 fct_rls_cb_data_t *rls_cb_data = NULL;
2675 fct_port_link_status_t *rls_resp;
2676 fct_i_local_port_t *iport = ICMD_TO_IPORT(icmd);
2677
2678 rls_cb_data = icmd->icmd_cb_private;
2679
2680 if (!FCT_IS_ELS_ACC(icmd)) {
2681 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_rls_cb: "
|
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <sys/sysmacros.h>
27 #include <sys/conf.h>
28 #include <sys/file.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/scsi/scsi.h>
33 #include <sys/scsi/impl/scsi_reset_notify.h>
34 #include <sys/disp.h>
35 #include <sys/byteorder.h>
36 #include <sys/varargs.h>
37 #include <sys/atomic.h>
38 #include <sys/sdt.h>
39
40 #include <sys/stmf.h>
41 #include <sys/stmf_ioctl.h>
42 #include <sys/portif.h>
43 #include <sys/fct.h>
130
131 /*
132 * All solicited and unsolicited ELS will be handled here
133 */
134 if (iport->iport_rpwe_head) {
135 suggested_action |= fct_walk_discovery_queue(iport);
136 }
137
138 /*
139 * We only process it when there's no outstanding link init CMD
140 */
141 if ((iport->iport_link_state == PORT_STATE_LINK_INIT_START) &&
142 !(iport->iport_li_state & (LI_STATE_FLAG_CMD_WAITING |
143 LI_STATE_FLAG_NO_LI_YET))) {
144 suggested_action |= fct_process_link_init(iport);
145 }
146
147 /*
148 * We process cmd aborting in the end
149 */
150 if (!list_is_empty(&iport->iport_abort_queue)) {
151 suggested_action |= fct_cmd_terminator(iport);
152 }
153
154 /*
155 * Check cmd max/free
156 */
157 if (iport->iport_cmdcheck_clock <= ddi_get_lbolt()) {
158 suggested_action |= fct_check_cmdlist(iport);
159 iport->iport_cmdcheck_clock = ddi_get_lbolt() +
160 drv_usectohz(FCT_CMDLIST_CHECK_SECONDS * 1000000);
161 iport->iport_max_active_ncmds = 0;
162 }
163
164 if (iport->iport_offline_prstate != FCT_OPR_DONE) {
165 suggested_action |= fct_handle_port_offline(iport);
166 }
167
168 if (suggested_action & DISC_ACTION_RESCAN) {
169 continue;
170 } else if (suggested_action & DISC_ACTION_DELAY_RESCAN) {
605 ASSERT(IPORT_FLOGI_DONE(iport));
606 ASSERT(iport->iport_link_info.port_topology ==
607 PORT_TOPOLOGY_PT_TO_PT);
608 if (iport->iport_li_state & LI_STATE_FLAG_CMD_RETCHECK) {
609 iport->iport_li_state &= ~LI_STATE_FLAG_CMD_RETCHECK;
610 if (iport->iport_li_comp_status != FCT_SUCCESS) {
611 iport->iport_login_retry++;
612 if (iport->iport_login_retry >= 3) {
613 stmf_trace(iport->iport_alias, "Failing"
614 " to PLOGI to remote port in N2N "
615 " ret=%llx, forcing link down",
616 iport->iport_li_comp_status);
617 mutex_exit(&iport->iport_worker_lock);
618 fct_handle_event(iport->iport_port,
619 FCT_EVENT_LINK_DOWN, 0, 0);
620 mutex_enter(&iport->iport_worker_lock);
621 }
622 }
623 }
624 /* Find out if we need to do PLOGI at all */
625 rw_enter(&iport->iport_lock, RW_READER);
626 if (iport->iport_nrps_login) {
627 iport->iport_li_state++;
628 atomic_and_32(&iport->iport_flags,
629 ~IPORT_ALLOW_UNSOL_FLOGI);
630 rw_exit(&iport->iport_lock);
631 goto check_state_again;
632 } else {
633 rw_exit(&iport->iport_lock);
634 }
635 if ((ddi_get_lbolt() >= iport->iport_li_cmd_timeout) &&
636 (!fct_lport_has_bigger_wwn(iport))) {
637 /* Cant wait forever */
638 stmf_trace(iport->iport_alias, "N2N: Remote port is "
639 "not logging in, forcing from our side");
640 force_login = 1;
641 } else {
642 force_login = 0;
643 }
644 if (force_login || fct_lport_has_bigger_wwn(iport)) {
645 elsop = ELS_OP_PLOGI;
646 wkdid = 1;
647 iport->iport_link_info.portid = 0xEF;
648 implicit = 0;
649 iport->iport_li_state |= LI_STATE_FLAG_CMD_RETCHECK;
650 } else {
651 ret = DISC_ACTION_DELAY_RESCAN;
652 }
653 break;
897 }
898 }
899
900 /*
901 * We always lookup by portid. port handles are too
902 * unreliable at this stage.
903 */
904 irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
905 if (els->els_req_payload[0] == ELS_OP_PLOGI) {
906 if (irp == NULL) {
907 /* drop the lock while we do allocations */
908 rw_exit(&iport->iport_lock);
909 rp = fct_alloc(FCT_STRUCT_REMOTE_PORT,
910 port->port_fca_rp_private_size, 0);
911 if (rp == NULL) {
912 fct_queue_cmd_for_termination(cmd,
913 FCT_ALLOC_FAILURE);
914 return;
915 }
916 irp = (fct_i_remote_port_t *)rp->rp_fct_private;
917 list_create(&irp->irp_els_list, sizeof (fct_i_cmd_t),
918 offsetof(fct_i_cmd_t, icmd_node));
919 rw_init(&irp->irp_lock, 0, RW_DRIVER, 0);
920 irp->irp_rp = rp;
921 irp->irp_portid = cmd->cmd_rportid;
922 rp->rp_port = port;
923 rp->rp_id = cmd->cmd_rportid;
924 rp->rp_handle = FCT_HANDLE_NONE;
925 /*
926 * Grab port lock as writer since we are going
927 * to modify the local port struct.
928 */
929 rw_enter(&iport->iport_lock, RW_WRITER);
930 /* Make sure nobody created the struct except us */
931 if (fct_portid_to_portptr(iport, cmd->cmd_rportid)) {
932 /* Oh well, free it */
933 fct_free(rp);
934 } else {
935 fct_queue_rp(iport, irp);
936 }
937 rw_downgrade(&iport->iport_lock);
938 /* Start over becasue we dropped the lock */
977 cmd_slot = fct_alloc_cmd_slot(iport, cmd);
978 if (cmd_slot == FCT_SLOT_EOL) {
979 /* This should not have happened */
980 rw_exit(&iport->iport_lock);
981 stmf_trace(iport->iport_alias,
982 "ran out of xchg resources");
983 fct_queue_cmd_for_termination(cmd,
984 FCT_NO_XCHG_RESOURCE);
985 return;
986 }
987 } else {
988 /*
989 * Tell the framework that fct_cmd_free() can decrement the
990 * irp_nonfcp_xchg_count variable.
991 */
992 atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
993 }
994 atomic_inc_16(&irp->irp_nonfcp_xchg_count);
995
996 /*
997 * The iport_lock is currently held as a Reader lock, protocol
998 * dictates that to modify iport_nrps_login the lock must be held
999 * as a Writer.
1000 */
1001 rw_exit(&iport->iport_lock);
1002 rw_enter(&iport->iport_lock, RW_WRITER);
1003
1004 /*
1005 * Grab the remote port lock while we modify the port state.
1006 * we should not drop the fca port lock (as a reader) until we
1007 * modify the remote port state.
1008 */
1009 rw_enter(&irp->irp_lock, RW_WRITER);
1010 if ((op == ELS_OP_PLOGI) || (op == ELS_OP_PRLI) ||
1011 (op == ELS_OP_LOGO) || (op == ELS_OP_PRLO) ||
1012 (op == ELS_OP_TPRLO)) {
1013 uint32_t rf = IRP_PRLI_DONE;
1014 if ((op == ELS_OP_PLOGI) || (op == ELS_OP_LOGO)) {
1015 rf |= IRP_PLOGI_DONE;
1016 if (irp->irp_flags & IRP_PLOGI_DONE)
1017 atomic_dec_32(&iport->iport_nrps_login);
1018 }
1019 atomic_inc_16(&irp->irp_sa_elses_count);
1020 atomic_and_32(&irp->irp_flags, ~rf);
1021 atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
1022 } else {
1023 atomic_inc_16(&irp->irp_nsa_elses_count);
1024 }
1055 for (i = 0; i < port->port_max_xchges; i++) {
1056 if (iport->iport_cmd_slots[i].slot_cmd == NULL)
1057 continue;
1058 icmd = iport->iport_cmd_slots[i].slot_cmd;
1059 if (icmd->icmd_flags & ICMD_IN_TRANSITION) {
1060 unhandled++;
1061 continue;
1062 }
1063
1064 if (icmd->icmd_flags & ICMD_CMD_COMPLETE) {
1065 unhandled++;
1066 continue;
1067 }
1068
1069 cmd = icmd->icmd_cmd;
1070 if (cmd->cmd_rp != rp) {
1071 skipped++;
1072 continue;
1073 }
1074 if (cmd->cmd_type & ttc) {
1075 if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1076 fct_queue_scsi_task_for_termination(cmd,
1077 FCT_ABORTED);
1078 } else {
1079 fct_cmd_unlink_els(irp, icmd);
1080 fct_q_for_termination_lock_held(iport, icmd,
1081 FCT_ABORTED);
1082 }
1083 cleaned++;
1084 } else {
1085 skipped++;
1086 }
1087 }
1088 if (((cleaned + skipped) == total) && (unhandled == 0)) {
1089 ret = 1;
1090 } else {
1091 /*
1092 * XXX: handle this situation.
1093 */
1094 stmf_trace(iport->iport_alias, "Clean up trouble for irp"
1095 " %p, c/s/u/t = %d/%d/%d/%d", irp, cleaned, skipped,
1096 unhandled, total);
1097 ret = 0;
1098 }
1099 if ((cleaned) && IS_WORKER_SLEEPING(iport))
1100 cv_signal(&iport->iport_worker_cv);
1101 mutex_exit(&iport->iport_worker_lock);
1102 rw_exit(&irp->irp_lock);
1103 rw_exit(&iport->iport_lock);
1104 return (ret);
1105 }
1106
1107 void
1108 fct_dequeue_els(fct_i_remote_port_t *irp)
1109 {
1110 fct_i_cmd_t *icmd;
1111
1112 rw_enter(&irp->irp_lock, RW_WRITER);
1113 icmd = list_remove_head(&irp->irp_els_list);
1114 atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_IRP_QUEUE);
1115 rw_exit(&irp->irp_lock);
1116 }
1117
1118 fct_status_t
1119 fct_register_remote_port(fct_local_port_t *port, fct_remote_port_t *rp,
1120 fct_cmd_t *cmd)
1121 {
1122 fct_status_t ret;
1123 fct_i_local_port_t *iport;
1124 fct_i_remote_port_t *irp;
1125 int i;
1126 char info[FCT_INFO_LEN];
1127
1128 iport = (fct_i_local_port_t *)port->port_fct_private;
1129 irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1130
1131 if ((ret = port->port_register_remote_port(port, rp, cmd)) !=
1132 FCT_SUCCESS)
1133 return (ret);
1188
1189 hba_fatal_err:;
1190 rw_exit(&irp->irp_lock);
1191 rw_exit(&iport->iport_lock);
1192 /*
1193 * XXX Throw HBA fatal error event
1194 */
1195 (void) fct_port_shutdown(iport->iport_port,
1196 STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1197 return (FCT_FAILURE);
1198 }
1199
1200 fct_status_t
1201 fct_deregister_remote_port(fct_local_port_t *port, fct_remote_port_t *rp)
1202 {
1203 fct_status_t ret = FCT_SUCCESS;
1204 fct_i_local_port_t *iport = PORT_TO_IPORT(port);
1205 fct_i_remote_port_t *irp = RP_TO_IRP(rp);
1206
1207 if (irp->irp_snn) {
1208 kmem_free(irp->irp_snn, irp->irp_snn_len);
1209 irp->irp_snn = NULL;
1210 irp->irp_snn_len = 0;
1211 }
1212 if (irp->irp_spn) {
1213 kmem_free(irp->irp_spn, irp->irp_spn_len);
1214 irp->irp_spn = NULL;
1215 irp->irp_spn_len = 0;
1216 }
1217
1218 if ((ret = port->port_deregister_remote_port(port, rp)) !=
1219 FCT_SUCCESS) {
1220 return (ret);
1221 }
1222
1223 if (irp->irp_flags & IRP_HANDLE_OPENED) {
1224 atomic_and_32(&irp->irp_flags, ~IRP_HANDLE_OPENED);
1225 iport->iport_rp_slots[rp->rp_handle] = NULL;
1226 }
1227 atomic_inc_64(&iport->iport_last_change);
1228 fct_log_remote_port_event(port, ESC_SUNFC_TARGET_REMOVE,
1229 rp->rp_pwwn, rp->rp_id);
1230
1231 return (FCT_SUCCESS);
1232 }
1233
1234 fct_status_t
1235 fct_send_accrjt(fct_cmd_t *cmd, uint8_t accrjt, uint8_t reason, uint8_t expl)
1250 return (port->port_send_cmd_response(cmd, 0));
1251 }
1252
1253
1254 disc_action_t
1255 fct_walk_discovery_queue(fct_i_local_port_t *iport)
1256 {
1257 char info[FCT_INFO_LEN];
1258 fct_i_remote_port_t **pirp;
1259 fct_i_remote_port_t *prev_irp = NULL;
1260 disc_action_t suggested_action = DISC_ACTION_NO_WORK;
1261 fct_i_remote_port_t *irp_dereg_list = NULL;
1262 fct_i_remote_port_t *irp_cur_item = NULL;
1263
1264 for (pirp = &iport->iport_rpwe_head; *pirp != NULL; ) {
1265 fct_i_remote_port_t *irp = *pirp;
1266 disc_action_t ret = DISC_ACTION_NO_WORK;
1267 int do_deregister = 0;
1268 int irp_deregister_timer = 0;
1269
1270 if (!list_is_empty(&irp->irp_els_list)) {
1271 ret |= fct_process_els(iport, irp);
1272 }
1273
1274 irp_deregister_timer = irp->irp_deregister_timer;
1275 if (irp_deregister_timer) {
1276 if (ddi_get_lbolt() >= irp_deregister_timer) {
1277 do_deregister = 1;
1278 } else {
1279 ret |= DISC_ACTION_DELAY_RESCAN;
1280 }
1281 }
1282 suggested_action |= ret;
1283
1284 if (list_is_empty(&irp->irp_els_list)) {
1285 mutex_exit(&iport->iport_worker_lock);
1286 rw_enter(&iport->iport_lock, RW_WRITER);
1287 rw_enter(&irp->irp_lock, RW_WRITER);
1288 mutex_enter(&iport->iport_worker_lock);
1289 if (list_is_empty(&irp->irp_els_list)) {
1290 if (!irp_deregister_timer ||
1291 (do_deregister &&
1292 !irp->irp_sa_elses_count &&
1293 !irp->irp_nsa_elses_count &&
1294 !irp->irp_fcp_xchg_count &&
1295 !irp->irp_nonfcp_xchg_count)) {
1296 /* dequeue irp from discovery queue */
1297 atomic_and_32(&irp->irp_flags,
1298 ~IRP_IN_DISCOVERY_QUEUE);
1299 *pirp = irp->irp_discovery_next;
1300 if (iport->iport_rpwe_head == NULL)
1301 iport->iport_rpwe_tail = NULL;
1302 else if (irp == iport->iport_rpwe_tail)
1303 iport->iport_rpwe_tail =
1304 prev_irp;
1305
1306 irp->irp_discovery_next = NULL;
1307 if (do_deregister) {
1308 fct_deque_rp(iport, irp);
1309 rw_exit(&irp->irp_lock);
1517 * a response to PLOGI. Because the initiator
1518 * will send a PRLI as soon as it responds to PLOGI.
1519 * Check fct_process_els() for more info.
1520 */
1521 atomic_or_32(&irp->irp_flags,
1522 IRP_SOL_PLOGI_IN_PROGRESS);
1523 atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
1524 ret = port->port_send_cmd(cmd);
1525 if (ret != FCT_SUCCESS) {
1526 atomic_and_32(&icmd->icmd_flags,
1527 ~ICMD_KNOWN_TO_FCA);
1528 atomic_and_32(&irp->irp_flags,
1529 ~IRP_SOL_PLOGI_IN_PROGRESS);
1530 }
1531 }
1532 }
1533 atomic_dec_16(&irp->irp_sa_elses_count);
1534
1535 if (ret == FCT_SUCCESS) {
1536 if (cmd_type == FCT_CMD_RCVD_ELS) {
1537 rw_enter(&iport->iport_lock, RW_WRITER);
1538 rw_enter(&irp->irp_lock, RW_WRITER);
1539 atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
1540 atomic_inc_32(&iport->iport_nrps_login);
1541 if (irp->irp_deregister_timer)
1542 irp->irp_deregister_timer = 0;
1543 rw_exit(&irp->irp_lock);
1544 rw_exit(&iport->iport_lock);
1545 }
1546 if (icmd_flags & ICMD_IMPLICIT) {
1547 DTRACE_FC_5(rport__login__end,
1548 fct_cmd_t, cmd,
1549 fct_local_port_t, port,
1550 fct_i_remote_port_t, irp,
1551 int, (cmd_type != FCT_CMD_RCVD_ELS),
1552 int, FCT_SUCCESS);
1553
1554 p = els->els_resp_payload;
1555 p[0] = ELS_OP_ACC;
1556 cmd->cmd_comp_status = FCT_SUCCESS;
1557 fct_send_cmd_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE);
1558 }
1559 } else {
1560 DTRACE_FC_5(rport__login__end,
1561 fct_cmd_t, cmd,
1562 fct_local_port_t, port,
1563 fct_i_remote_port_t, irp,
1564 int, (cmd_type != FCT_CMD_RCVD_ELS),
2007 if (ret != FCT_SUCCESS) {
2008 fct_queue_cmd_for_termination(icmd->icmd_cmd, ret);
2009 } else {
2010 if (fct_rscn_options & RSCN_OPTION_VERIFY) {
2011 fct_rscn_verify(iport, rscn_req_payload,
2012 rscn_req_size);
2013 }
2014 }
2015
2016 kmem_free(rscn_req_payload, rscn_req_size);
2017 } else {
2018 ASSERT(0);
2019 }
2020
2021 return (DISC_ACTION_RESCAN);
2022 }
2023
2024 disc_action_t
2025 fct_process_els(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
2026 {
2027 list_t cmd_to_abort;
2028 fct_i_cmd_t *next, *icmd;
2029 fct_cmd_t *cmd;
2030 fct_els_t *els;
2031 int dq;
2032 disc_action_t ret = DISC_ACTION_NO_WORK;
2033 uint8_t op;
2034
2035 mutex_exit(&iport->iport_worker_lock);
2036
2037 /*
2038 * Do some cleanup based on the following.
2039 * - We can only have one session affecting els pending.
2040 * - If any session affecting els is pending no other els is allowed.
2041 * - If PLOGI is not done, nothing except PLOGI or LOGO is allowed.
2042 * NOTE: If port is down the cleanup is done outside of this
2043 * function.
2044 * NOTE: There is a side effect, if a sa ELS (non PLOGI) is received
2045 * while a PLOGI is pending, it will kill itself and the PLOGI.
2046 * which is probably ok.
2047 */
2048 rw_enter(&irp->irp_lock, RW_WRITER);
2049 icmd = list_head(&irp->irp_els_list);
2050 list_create(&cmd_to_abort, sizeof (fct_i_cmd_t),
2051 offsetof(fct_i_cmd_t, icmd_node));
2052 while (icmd != NULL) {
2053 int special_prli_cond = 0;
2054 dq = 0;
2055
2056 els = (fct_els_t *)(icmd->icmd_cmd)->cmd_specific;
2057
2058 if ((icmd->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS) &&
2059 (els->els_req_payload[0] == ELS_OP_PRLI) &&
2060 (irp->irp_flags & IRP_SOL_PLOGI_IN_PROGRESS)) {
2061 /*
2062 * The initiator sent a PRLI right after responding
2063 * to PLOGI and we have not yet finished processing
2064 * the PLOGI completion. We should not kill the PRLI
2065 * as the initiator may not retry it.
2066 */
2067 special_prli_cond = 1;
2068 }
2069
2070 if (icmd->icmd_flags & ICMD_BEING_ABORTED) {
2071 dq = 1;
2072 } else if (irp->irp_sa_elses_count > 1) {
2073 dq = 1;
2074 /* This els might have set the CLEANUP flag */
2075 atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
2076 stmf_trace(iport->iport_alias, "Killing ELS %x cond 1",
2077 els->els_req_payload[0]);
2078 } else if (irp->irp_sa_elses_count &&
2079 ((icmd->icmd_flags & ICMD_SESSION_AFFECTING) == 0)) {
2080 stmf_trace(iport->iport_alias, "Killing ELS %x cond 2",
2081 els->els_req_payload[0]);
2082 dq = 1;
2083 } else if (((irp->irp_flags & IRP_PLOGI_DONE) == 0) &&
2084 (els->els_req_payload[0] != ELS_OP_PLOGI) &&
2085 (els->els_req_payload[0] != ELS_OP_LOGO) &&
2086 (special_prli_cond == 0)) {
2087 stmf_trace(iport->iport_alias, "Killing ELS %x cond 3",
2088 els->els_req_payload[0]);
2089 dq = 1;
2090 }
2091
2092 next = list_next(&irp->irp_els_list, icmd);
2093 if (dq) {
2094 list_remove(&irp->irp_els_list, icmd);
2095 if (icmd->icmd_flags & ICMD_SESSION_AFFECTING)
2096 atomic_dec_16(&irp->irp_sa_elses_count);
2097 else
2098 atomic_dec_16(&irp->irp_nsa_elses_count);
2099 list_insert_head(&cmd_to_abort, icmd);
2100 }
2101 icmd = next;
2102 }
2103 rw_exit(&irp->irp_lock);
2104
2105 while (!list_is_empty(&cmd_to_abort)) {
2106 fct_i_cmd_t *c = list_remove_head(&cmd_to_abort);
2107
2108 atomic_and_32(&c->icmd_flags, ~ICMD_IN_IRP_QUEUE);
2109 fct_queue_cmd_for_termination(c->icmd_cmd, FCT_ABORTED);
2110 }
2111
2112 /*
2113 * pick from the top of the queue
2114 */
2115 icmd = list_head(&irp->irp_els_list);
2116 if (icmd == NULL) {
2117 /*
2118 * The cleanup took care of everything.
2119 */
2120
2121 mutex_enter(&iport->iport_worker_lock);
2122 return (DISC_ACTION_RESCAN);
2123 }
2124
2125 cmd = icmd->icmd_cmd;
2126 els = ICMD_TO_ELS(icmd);
2127 op = els->els_req_payload[0];
2128 if ((icmd->icmd_flags & ICMD_ELS_PROCESSING_STARTED) == 0) {
2129 stmf_trace(iport->iport_alias, "Processing %ssol ELS %x (%s) "
2130 "rp_id=%x", (cmd->cmd_type == FCT_CMD_RCVD_ELS) ? "un" : "",
2131 op, FCT_ELS_NAME(op), cmd->cmd_rportid);
2132 atomic_or_32(&icmd->icmd_flags, ICMD_ELS_PROCESSING_STARTED);
2133 }
2134
2135 if (op == ELS_OP_PLOGI) {
2181 }
2182
2183 void
2184 fct_handle_sol_els_completion(fct_i_local_port_t *iport, fct_i_cmd_t *icmd)
2185 {
2186 fct_i_remote_port_t *irp = NULL;
2187 fct_els_t *els = ICMD_TO_ELS(icmd);
2188 uint8_t op = els->els_req_payload[0];
2189
2190 if (icmd->icmd_cmd->cmd_rp) {
2191 irp = ICMD_TO_IRP(icmd);
2192 }
2193 if (icmd->icmd_cmd->cmd_rp &&
2194 (icmd->icmd_cmd->cmd_comp_status == FCT_SUCCESS) &&
2195 (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2196 bcopy(els->els_resp_payload + 20, irp->irp_rp->rp_pwwn, 8);
2197 bcopy(els->els_resp_payload + 28, irp->irp_rp->rp_nwwn, 8);
2198
2199 stmf_wwn_to_devid_desc((scsi_devid_desc_t *)irp->irp_id,
2200 irp->irp_rp->rp_pwwn, PROTOCOL_FIBRE_CHANNEL);
2201 rw_enter(&iport->iport_lock, RW_WRITER);
2202 rw_enter(&irp->irp_lock, RW_WRITER);
2203 atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
2204 atomic_inc_32(&iport->iport_nrps_login);
2205 if (irp->irp_deregister_timer) {
2206 irp->irp_deregister_timer = 0;
2207 irp->irp_dereg_count = 0;
2208 }
2209 rw_exit(&irp->irp_lock);
2210 rw_exit(&iport->iport_lock);
2211 }
2212
2213 if (irp && (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2214 rw_enter(&irp->irp_lock, RW_WRITER);
2215 atomic_and_32(&irp->irp_flags, ~IRP_SOL_PLOGI_IN_PROGRESS);
2216 rw_exit(&irp->irp_lock);
2217 }
2218 atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2219 stmf_trace(iport->iport_alias, "Sol ELS %x (%s) completed with "
2220 "status %llx, did/%x", op, FCT_ELS_NAME(op),
2221 icmd->icmd_cmd->cmd_comp_status, icmd->icmd_cmd->cmd_rportid);
2222 }
2223
2224 static disc_action_t
2225 fct_check_cmdlist(fct_i_local_port_t *iport)
2226 {
2227 int num_to_release, ndx;
2228 fct_i_cmd_t *icmd;
2229 uint32_t total, max_active;
2230
2231 ASSERT(MUTEX_HELD(&iport->iport_worker_lock));
2232
2233 total = iport->iport_total_alloced_ncmds;
2234 max_active = iport->iport_max_active_ncmds;
2235
2236 if (total <= max_active)
2237 return (DISC_ACTION_NO_WORK);
2238 /*
2239 * Everytime, we release half of the difference
2240 */
2241 num_to_release = (total + 1 - max_active) / 2;
2242
2243 mutex_exit(&iport->iport_worker_lock);
2244 for (ndx = 0; ndx < num_to_release; ndx++) {
2245 mutex_enter(&iport->iport_cached_cmd_lock);
2246 if (list_is_empty(&iport->iport_cached_cmdlist)) {
2247 mutex_exit(&iport->iport_cached_cmd_lock);
2248 break;
2249 }
2250 icmd = list_remove_head(&iport->iport_cached_cmdlist);
2251 iport->iport_cached_ncmds--;
2252 mutex_exit(&iport->iport_cached_cmd_lock);
2253 atomic_dec_32(&iport->iport_total_alloced_ncmds);
2254 fct_free(icmd->icmd_cmd);
2255 }
2256 mutex_enter(&iport->iport_worker_lock);
2257 return (DISC_ACTION_RESCAN);
2258 }
2259
2260 /*
2261 * The efficiency of handling solicited commands is very low here. But
2262 * fortunately, we seldom send solicited commands. So it will not hurt
2263 * the system performance much.
2264 */
2265 static disc_action_t
2266 fct_check_solcmd_queue(fct_i_local_port_t *iport)
2267 {
2268 fct_i_cmd_t *icmd = NULL;
2269 fct_i_cmd_t *prev_icmd = NULL;
2270 fct_i_cmd_t *next_icmd = NULL;
2299 * mutex ???
2300 */
2301 icmd->icmd_cb(icmd);
2302 }
2303
2304
2305 /*
2306 * Release resources for this solicited cmd
2307 */
2308 if (iport->iport_solcmd_queue == icmd) {
2309 iport->iport_solcmd_queue = next_icmd;
2310 } else {
2311 prev_icmd = iport->iport_solcmd_queue;
2312 while (prev_icmd->icmd_solcmd_next != icmd) {
2313 prev_icmd = prev_icmd->icmd_solcmd_next;
2314 }
2315 prev_icmd->icmd_solcmd_next = next_icmd;
2316 }
2317
2318 icmd->icmd_cb = NULL;
2319
2320 /*
2321 * If the command has none-zero icmd_node pointers
2322 * it means it's been linked onto the iport_abort_queue.
2323 * Since the iport_worker_lock is held the command
2324 * can be removed before it's freed.
2325 */
2326 if (icmd->icmd_node.list_next != NULL) {
2327 list_remove(&iport->iport_abort_queue, icmd);
2328 }
2329
2330 mutex_exit(&iport->iport_worker_lock);
2331 fct_cmd_free(icmd->icmd_cmd);
2332 mutex_enter(&iport->iport_worker_lock);
2333 } else {
2334 /*
2335 * This solicited cmd is still ongoing.
2336 * We need check if it's time to abort this cmd
2337 */
2338 if (((icmd->icmd_start_time + drv_usectohz(
2339 USEC_SOL_TIMEOUT)) < ddi_get_lbolt()) &&
2340 !(icmd->icmd_flags & ICMD_BEING_ABORTED)) {
2341 fct_q_for_termination_lock_held(iport,
2342 icmd, FCT_ABORTED);
2343 }
2344 }
2345 }
2346
2347 return (DISC_ACTION_DELAY_RESCAN);
2348 }
2349
2446
2447 rw_enter(&ICMD_TO_IPORT(icmd)->iport_lock, RW_READER);
2448 mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2449 query_irp = fct_lookup_irp_by_nodewwn(ICMD_TO_IPORT(icmd),
2450 ICMD_TO_CT(icmd)->ct_req_payload + 16);
2451
2452 if (!query_irp) {
2453 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2454 "can't get rp icmd-%p", icmd);
2455 goto exit_gsnn_cb;
2456 } else {
2457 snlen = ICMD_TO_CT(icmd)->ct_resp_payload[16];
2458 }
2459
2460 if (query_irp && snlen) {
2461 /*
2462 * Release previous resource, then allocate needed resource
2463 */
2464 sn = query_irp->irp_snn;
2465 if (sn) {
2466 kmem_free(sn, query_irp->irp_snn_len);
2467 }
2468
2469 query_irp->irp_snn = NULL;
2470 query_irp->irp_snn_len = 0;
2471 sn = kmem_zalloc(snlen + 1, KM_SLEEP);
2472 (void) strncpy(sn, (char *)
2473 ICMD_TO_CT(icmd)->ct_resp_payload + 17, snlen);
2474 if (strlen(sn) != snlen) {
2475 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias,
2476 "fct_gsnn_cb: %s, but len=%d", sn, snlen);
2477 kmem_free(sn, snlen + 1);
2478 sn = NULL;
2479 }
2480
2481 /*
2482 * Update symbolic node name
2483 */
2484 query_irp->irp_snn = sn;
2485 if (sn != NULL)
2486 query_irp->irp_snn_len = snlen + 1;
2487 if ((query_irp->irp_flags & IRP_SCSI_SESSION_STARTED) &&
2488 (query_irp->irp_session)) {
2489 query_irp->irp_session->ss_rport_alias =
2490 query_irp->irp_snn;
2491 }
2492 } else {
2493 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2494 "irp/%p, snlen/%d", query_irp, snlen);
2495 }
2496
2497 exit_gsnn_cb:
2498 rw_exit(&ICMD_TO_IPORT(icmd)->iport_lock);
2499 }
2500
2501 void
2502 fct_link_init_cb(fct_i_cmd_t *icmd)
2503 {
2504 fct_i_local_port_t *iport = ICMD_TO_IPORT(icmd);
2505
2506 iport->iport_li_state &= ~LI_STATE_FLAG_CMD_WAITING;
2677
2678 if (!FCT_IS_CT_ACC(icmd)) {
2679 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gspn_cb: "
2680 "GSPN_ID is not accepted by NS - icmd/%p", icmd);
2681 return;
2682 }
2683 mutex_exit(&iport->iport_worker_lock);
2684
2685 resp = ct->ct_resp_payload;
2686 req = ct->ct_req_payload;
2687 query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2688
2689 rw_enter(&iport->iport_lock, RW_READER);
2690 mutex_enter(&iport->iport_worker_lock);
2691 query_irp = fct_portid_to_portptr(iport, query_portid);
2692 if (query_irp) {
2693 spnlen = resp[16];
2694 if (spnlen > 0) {
2695 if (query_irp->irp_spn) {
2696 kmem_free(query_irp->irp_spn,
2697 query_irp->irp_spn_len);
2698 }
2699 query_irp->irp_spn_len = spnlen + 1;
2700 query_irp->irp_spn = kmem_zalloc(
2701 query_irp->irp_spn_len, KM_SLEEP);
2702 (void) strncpy(query_irp->irp_spn,
2703 (char *)resp + 17, spnlen);
2704 }
2705 }
2706 rw_exit(&iport->iport_lock);
2707 }
2708
2709 void
2710 fct_rls_cb(fct_i_cmd_t *icmd)
2711 {
2712 fct_els_t *els = ICMD_TO_ELS(icmd);
2713 uint8_t *resp;
2714 fct_rls_cb_data_t *rls_cb_data = NULL;
2715 fct_port_link_status_t *rls_resp;
2716 fct_i_local_port_t *iport = ICMD_TO_IPORT(icmd);
2717
2718 rls_cb_data = icmd->icmd_cb_private;
2719
2720 if (!FCT_IS_ELS_ACC(icmd)) {
2721 stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_rls_cb: "
|