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: "
 
 |