Print this page
6742 Freed and reused idm_conn_t buffer leads to system panic.
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Steve Ping <steve.ping@nexenta.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>


   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 /*
  23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2013 by Delphix. All rights reserved.

  25  */
  26 
  27 #include <sys/cpuvar.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/modctl.h>
  31 #include <sys/socket.h>
  32 #include <sys/strsubr.h>
  33 #include <sys/note.h>
  34 #include <sys/sdt.h>
  35 
  36 #define IDM_CONN_SM_STRINGS
  37 #define IDM_CN_NOTIFY_STRINGS
  38 #include <sys/idm/idm.h>
  39 
  40 boolean_t       idm_sm_logging = B_FALSE;
  41 
  42 extern idm_global_t     idm; /* Global state */
  43 
  44 static void


 153 idm_conn_sm_fini(idm_conn_t *ic)
 154 {
 155 
 156         /*
 157          * The connection may only be partially created. If there
 158          * is no taskq, then the connection SM was not initialized.
 159          */
 160         if (ic->ic_state_taskq == NULL) {
 161                 return;
 162         }
 163 
 164         taskq_destroy(ic->ic_state_taskq);
 165 
 166         cv_destroy(&ic->ic_state_cv);
 167         /*
 168          * The thread that generated the event that got us here may still
 169          * hold the ic_state_mutex. Once it is released we can safely
 170          * destroy it since there is no way to locate the object now.
 171          */
 172         mutex_enter(&ic->ic_state_mutex);

 173         mutex_destroy(&ic->ic_state_mutex);
 174 }
 175 
 176 void
 177 idm_conn_event(idm_conn_t *ic, idm_conn_event_t event, uintptr_t event_info)
 178 {
 179         mutex_enter(&ic->ic_state_mutex);
 180         idm_conn_event_locked(ic, event, event_info, CT_NONE);
 181         mutex_exit(&ic->ic_state_mutex);
 182 }
 183 
 184 
 185 idm_status_t
 186 idm_conn_reinstate_event(idm_conn_t *old_ic, idm_conn_t *new_ic)
 187 {
 188         int result;
 189 
 190         mutex_enter(&old_ic->ic_state_mutex);
 191         if (((old_ic->ic_conn_type == CONN_TYPE_INI) &&
 192             (old_ic->ic_state != CS_S8_CLEANUP)) ||


 474         case CE_TRANSPORT_FAIL:
 475         case CE_CONNECT_FAIL:
 476         case CE_LOGOUT_OTHER_CONN_RCV:
 477         case CE_TX_PROTOCOL_ERROR:
 478         case CE_RX_PROTOCOL_ERROR:
 479                 /* T2 */
 480                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 481                 break;
 482         default:
 483                 ASSERT(0);
 484                 /*NOTREACHED*/
 485         }
 486 }
 487 
 488 
 489 static void
 490 idm_login_timeout(void *arg)
 491 {
 492         idm_conn_t *ic = arg;
 493 

 494         idm_conn_event(ic, CE_LOGIN_TIMEOUT, NULL);
 495 }
 496 
 497 static void
 498 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 499 {
 500         switch (event_ctx->iec_event) {
 501         case CE_LOGIN_RCV:
 502                 /* T4 */

 503                 idm_initial_login_actions(ic, event_ctx);
 504                 idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx);
 505                 break;
 506         case CE_LOGIN_TIMEOUT:
 507                 /*
 508                  * Don't need to cancel login timer since the timer is
 509                  * presumed to be the source of this event.
 510                  */
 511                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 512                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 513                 break;
 514         case CE_CONNECT_REJECT:
 515                 /*
 516                  * Iscsit doesn't want to hear from us again in this case.
 517                  * Since it rejected the connection it doesn't have a
 518                  * connection context to handle additional notifications.
 519                  * IDM needs to just clean things up on its own.
 520                  */
 521                 (void) untimeout(ic->ic_state_timeout);
 522                 idm_update_state(ic, CS_S9A_REJECTED, event_ctx);
 523                 break;
 524         case CE_CONNECT_FAIL:
 525         case CE_TRANSPORT_FAIL:
 526         case CE_LOGOUT_OTHER_CONN_SND:
 527                 /* T6 */
 528                 (void) untimeout(ic->ic_state_timeout);
 529                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 530                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 531                 break;
 532         case CE_TX_PROTOCOL_ERROR:
 533         case CE_RX_PROTOCOL_ERROR:
 534                 /* Don't care */
 535                 break;
 536         default:
 537                 ASSERT(0);
 538                 /*NOTREACHED*/
 539         }
 540 }
 541 
 542 static void
 543 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 544 {
 545         idm_pdu_t *pdu;
 546 
 547         /*
 548          * Login timer should no longer be active after leaving this
 549          * state.
 550          */
 551         switch (event_ctx->iec_event) {
 552         case CE_LOGIN_SUCCESS_RCV:
 553         case CE_LOGIN_SUCCESS_SND:
 554                 ASSERT(ic->ic_client_callback == NULL);
 555 
 556                 (void) untimeout(ic->ic_state_timeout);
 557                 idm_login_success_actions(ic, event_ctx);
 558                 if (ic->ic_rdma_extensions) {
 559                         /* T19 */
 560                         idm_update_state(ic, CS_S12_ENABLE_DM, event_ctx);
 561                 } else {
 562                         /* T5 */
 563                         idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx);
 564                 }
 565                 break;
 566         case CE_LOGIN_TIMEOUT:
 567                 /* T7 */
 568                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 569                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 570                 break;
 571         case CE_LOGIN_FAIL_SND:
 572                 /*
 573                  * Allow the logout response pdu to be sent and defer
 574                  * the state machine cleanup until the completion callback.
 575                  * Only 1 level or callback interposition is allowed.
 576                  */
 577                 (void) untimeout(ic->ic_state_timeout);
 578                 pdu = (idm_pdu_t *)event_ctx->iec_info;
 579                 ASSERT(ic->ic_client_callback == NULL);
 580                 ic->ic_client_callback = pdu->isp_callback;
 581                 pdu->isp_callback =
 582                     idm_state_s9b_wait_snd_done_cb;
 583                 idm_update_state(ic, CS_S9B_WAIT_SND_DONE,
 584                     event_ctx);
 585                 break;
 586         case CE_LOGIN_FAIL_RCV:
 587                 ASSERT(ic->ic_client_callback == NULL);
 588                 /*
 589                  * Need to deliver this PDU to the initiator now because after
 590                  * we update the state to CS_S9_INIT_ERROR the initiator will
 591                  * no longer be in an appropriate state.
 592                  */
 593                 event_ctx->iec_pdu_forwarded = B_TRUE;
 594                 pdu = (idm_pdu_t *)event_ctx->iec_info;
 595                 idm_pdu_rx_forward(ic, pdu);
 596                 /* FALLTHROUGH */
 597         case CE_TRANSPORT_FAIL:
 598         case CE_LOGOUT_OTHER_CONN_SND:
 599         case CE_LOGOUT_OTHER_CONN_RCV:
 600                 /* T7 */
 601                 (void) untimeout(ic->ic_state_timeout);
 602                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 603                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 604                 break;
 605         case CE_LOGOUT_SESSION_SUCCESS:
 606                 /*
 607                  * T8
 608                  * A session reinstatement request can be received while a
 609                  * session is active and a login is in process. The iSCSI
 610                  * connections are shut down by a CE_LOGOUT_SESSION_SUCCESS
 611                  * event sent from the session to the IDM layer.
 612                  */
 613                 (void) untimeout(ic->ic_state_timeout);
 614                 if (IDM_CONN_ISTGT(ic)) {
 615                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
 616                 } else {
 617                         ic->ic_transport_ops->it_ini_conn_disconnect(ic);
 618                 }
 619                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
 620                 break;
 621 
 622         case CE_LOGIN_SND:
 623                 ASSERT(ic->ic_client_callback == NULL);
 624                 /*
 625                  * Initiator connections will see initial login PDU
 626                  * in this state.  Target connections see initial
 627                  * login PDU in "xpt up" state.
 628                  */
 629                 mutex_enter(&ic->ic_state_mutex);
 630                 if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) {
 631                         idm_initial_login_actions(ic, event_ctx);
 632                 }
 633                 mutex_exit(&ic->ic_state_mutex);


 809                 idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
 810                 break;
 811         case CE_TX_PROTOCOL_ERROR:
 812         case CE_RX_PROTOCOL_ERROR:
 813         case CE_MISC_TX:
 814         case CE_MISC_RX:
 815         case CE_LOGIN_TIMEOUT:
 816                 /* Don't care */
 817                 break;
 818         default:
 819                 ASSERT(0);
 820         }
 821 }
 822 
 823 
 824 static void
 825 idm_logout_req_timeout(void *arg)
 826 {
 827         idm_conn_t *ic = arg;
 828 

 829         idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL);
 830 }
 831 
 832 static void
 833 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 834 {
 835         /* Must cancel logout timer before leaving this state */
 836         switch (event_ctx->iec_event) {
 837         case CE_LOGOUT_THIS_CONN_RCV:
 838         case CE_LOGOUT_THIS_CONN_SND:
 839         case CE_LOGOUT_OTHER_CONN_RCV:
 840         case CE_LOGOUT_OTHER_CONN_SND:
 841                 /* T10 */
 842                 if (IDM_CONN_ISTGT(ic)) {
 843                         (void) untimeout(ic->ic_state_timeout);
 844                 }
 845                 idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */
 846                 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
 847                 break;
 848         case CE_LOGOUT_SESSION_RCV:
 849         case CE_LOGOUT_SESSION_SND:
 850                 /* T10 */
 851                 if (IDM_CONN_ISTGT(ic)) {
 852                         (void) untimeout(ic->ic_state_timeout);
 853                 }
 854                 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
 855                 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
 856                 break;
 857         case CE_ASYNC_LOGOUT_RCV:
 858         case CE_ASYNC_LOGOUT_SND:
 859                 /* T12 Do nothing */
 860                 break;
 861         case CE_TRANSPORT_FAIL:
 862         case CE_ASYNC_DROP_CONN_RCV:
 863         case CE_ASYNC_DROP_CONN_SND:
 864         case CE_ASYNC_DROP_ALL_CONN_RCV:
 865         case CE_ASYNC_DROP_ALL_CONN_SND:
 866                 /* T16 */
 867                 if (IDM_CONN_ISTGT(ic)) {
 868                         (void) untimeout(ic->ic_state_timeout);
 869                 }
 870                 /* FALLTHROUGH */
 871         case CE_LOGOUT_TIMEOUT:
 872                 idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */
 873                 idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
 874                 break;
 875         case CE_LOGOUT_SESSION_SUCCESS:
 876                 /* T18 */
 877                 if (IDM_CONN_ISTGT(ic)) {
 878                         (void) untimeout(ic->ic_state_timeout);
 879                 }
 880                 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
 881 
 882                 /* Close connection (if it's not already closed) */
 883                 if (IDM_CONN_ISTGT(ic)) {
 884                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
 885                 } else {
 886                         ic->ic_transport_ops->it_ini_conn_disconnect(ic);
 887                 }
 888 
 889                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
 890                 break;
 891         case CE_TX_PROTOCOL_ERROR:
 892         case CE_RX_PROTOCOL_ERROR:
 893         case CE_MISC_TX:
 894         case CE_MISC_RX:
 895         case CE_LOGIN_TIMEOUT:
 896                 /* Don't care */
 897                 break;
 898         default:
 899                 ASSERT(0);
 900         }
 901 }
 902 
 903 
 904 static void
 905 idm_cleanup_timeout(void *arg)
 906 {
 907         idm_conn_t *ic = arg;
 908 

 909         idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL);
 910 }
 911 
 912 static void
 913 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 914 {
 915         idm_pdu_t *pdu;
 916 
 917         /*
 918          * Need to cancel the cleanup timeout before leaving this state
 919          * if it hasn't already fired.
 920          */
 921         switch (event_ctx->iec_event) {
 922         case CE_LOGOUT_SUCCESS_RCV:
 923         case CE_LOGOUT_SUCCESS_SND:
 924         case CE_LOGOUT_SESSION_SUCCESS:
 925                 (void) untimeout(ic->ic_state_timeout);
 926                 /*FALLTHROUGH*/
 927         case CE_CLEANUP_TIMEOUT:
 928                 /* M1 */
 929                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
 930                 break;
 931         case CE_LOGOUT_OTHER_CONN_RCV:
 932         case CE_LOGOUT_OTHER_CONN_SND:
 933                 /* M2 */
 934                 idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx);
 935                 break;
 936         case CE_LOGOUT_SUCCESS_SND_DONE:
 937         case CE_LOGOUT_FAIL_SND_DONE:
 938                 pdu = (idm_pdu_t *)event_ctx->iec_info;
 939                 /* restore client callback */
 940                 pdu->isp_callback =  ic->ic_client_callback;
 941                 ic->ic_client_callback = NULL;
 942                 idm_pdu_complete(pdu, pdu->isp_status);
 943                 break;
 944         case CE_LOGOUT_SESSION_RCV:
 945         case CE_LOGOUT_SESSION_SND:


1014 
1015 
1016 
1017 static void
1018 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1019 {
1020         idm_pdu_t *pdu;
1021 
1022         /*
1023          * Need to cancel the cleanup timeout before leaving this state
1024          * if it hasn't already fired.
1025          */
1026         switch (event_ctx->iec_event) {
1027         case CE_LOGOUT_FAIL_RCV:
1028         case CE_LOGOUT_FAIL_SND:
1029                 idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
1030                 break;
1031         case CE_LOGOUT_SUCCESS_SND:
1032         case CE_LOGOUT_SUCCESS_RCV:
1033         case CE_LOGOUT_SESSION_SUCCESS:
1034                 (void) untimeout(ic->ic_state_timeout);
1035                 /*FALLTHROUGH*/
1036         case CE_CLEANUP_TIMEOUT:
1037                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
1038                 break;
1039         case CE_LOGOUT_SUCCESS_SND_DONE:
1040         case CE_LOGOUT_FAIL_SND_DONE:
1041                 pdu = (idm_pdu_t *)event_ctx->iec_info;
1042                 /* restore client callback */
1043                 pdu->isp_callback =  ic->ic_client_callback;
1044                 ic->ic_client_callback = NULL;
1045                 idm_pdu_complete(pdu, pdu->isp_status);
1046                 break;
1047         case CE_TX_PROTOCOL_ERROR:
1048         case CE_RX_PROTOCOL_ERROR:
1049         case CE_MISC_TX:
1050         case CE_MISC_RX:
1051         case CE_LOGIN_TIMEOUT:
1052         case CE_LOGOUT_TIMEOUT:
1053                 /* Don't care */
1054                 break;


1170                  * Finish any connection related setup including
1171                  * waking up the idm_tgt_conn_accept thread.
1172                  * and starting the login timer.  If the function
1173                  * fails then we return to "free" state.
1174                  */
1175                 if ((rc = idm_tgt_conn_finish(ic)) != IDM_STATUS_SUCCESS) {
1176                         switch (rc) {
1177                         case IDM_STATUS_REJECT:
1178                                 idm_conn_event(ic, CE_CONNECT_REJECT, NULL);
1179                                 break;
1180                         default:
1181                                 idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1182                                 break;
1183                         }
1184                 }
1185 
1186                 /*
1187                  * First login received will cause a transition to
1188                  * CS_S4_IN_LOGIN.  Start login timer.
1189                  */

1190                 ic->ic_state_timeout = timeout(idm_login_timeout, ic,
1191                     drv_usectohz(IDM_LOGIN_SECONDS*1000000));
1192                 break;
1193         case CS_S4_IN_LOGIN:
1194                 if (ic->ic_conn_type == CONN_TYPE_INI) {
1195                         (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
1196                         mutex_enter(&ic->ic_state_mutex);
1197                         ic->ic_state_flags |= CF_LOGIN_READY;
1198                         cv_signal(&ic->ic_state_cv);
1199                         mutex_exit(&ic->ic_state_mutex);
1200                 }
1201                 break;
1202         case CS_S5_LOGGED_IN:
1203                 ASSERT(!ic->ic_ffp);
1204                 /*
1205                  * IDM can go to FFP before the initiator but it
1206                  * needs to go to FFP after the target (IDM target should
1207                  * go to FFP after notify_ack).
1208                  */
1209                 idm_status = idm_ffp_enable(ic);
1210                 if (idm_status != IDM_STATUS_SUCCESS) {
1211                         idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
1212                 }
1213 
1214                 if (ic->ic_reinstate_conn) {
1215                         /* Connection reinstatement is complete */
1216                         idm_conn_event(ic->ic_reinstate_conn,
1217                             CE_CONN_REINSTATE_SUCCESS, NULL);
1218                 }
1219                 break;
1220         case CS_S6_IN_LOGOUT:
1221                 break;
1222         case CS_S7_LOGOUT_REQ:
1223                 /* Start logout timer for target connections */
1224                 if (IDM_CONN_ISTGT(ic)) {

1225                         ic->ic_state_timeout = timeout(idm_logout_req_timeout,
1226                             ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000));
1227                 }
1228                 break;
1229         case CS_S8_CLEANUP:
1230                 /* Close connection (if it's not already closed) */
1231                 if (IDM_CONN_ISTGT(ic)) {
1232                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1233                 } else {
1234                         ic->ic_transport_ops->it_ini_conn_disconnect(ic);
1235                 }
1236 
1237                 /* Stop executing active tasks */
1238                 idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND);
1239 
1240                 /* Start logout timer */

1241                 ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic,
1242                     drv_usectohz(IDM_CLEANUP_SECONDS*1000000));
1243                 break;
1244         case CS_S10_IN_CLEANUP:
1245                 break;
1246         case CS_S9A_REJECTED:
1247                 /*
1248                  * We never finished establishing the connection so no
1249                  * disconnect.  No client notifications because the client
1250                  * rejected the connection.
1251                  */
1252                 idm_refcnt_async_wait_ref(&ic->ic_refcnt,
1253                     &idm_conn_reject_unref);
1254                 break;
1255         case CS_S9B_WAIT_SND_DONE:
1256                 break;
1257         case CS_S9_INIT_ERROR:
1258                 if (IDM_CONN_ISTGT(ic)) {
1259                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1260                 } else {




   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 /*
  23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2013 by Delphix. All rights reserved.
  25  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 #include <sys/cpuvar.h>
  29 #include <sys/ddi.h>
  30 #include <sys/sunddi.h>
  31 #include <sys/modctl.h>
  32 #include <sys/socket.h>
  33 #include <sys/strsubr.h>
  34 #include <sys/note.h>
  35 #include <sys/sdt.h>
  36 
  37 #define IDM_CONN_SM_STRINGS
  38 #define IDM_CN_NOTIFY_STRINGS
  39 #include <sys/idm/idm.h>
  40 
  41 boolean_t       idm_sm_logging = B_FALSE;
  42 
  43 extern idm_global_t     idm; /* Global state */
  44 
  45 static void


 154 idm_conn_sm_fini(idm_conn_t *ic)
 155 {
 156 
 157         /*
 158          * The connection may only be partially created. If there
 159          * is no taskq, then the connection SM was not initialized.
 160          */
 161         if (ic->ic_state_taskq == NULL) {
 162                 return;
 163         }
 164 
 165         taskq_destroy(ic->ic_state_taskq);
 166 
 167         cv_destroy(&ic->ic_state_cv);
 168         /*
 169          * The thread that generated the event that got us here may still
 170          * hold the ic_state_mutex. Once it is released we can safely
 171          * destroy it since there is no way to locate the object now.
 172          */
 173         mutex_enter(&ic->ic_state_mutex);
 174         IDM_SM_TIMER_CLEAR(ic);
 175         mutex_destroy(&ic->ic_state_mutex);
 176 }
 177 
 178 void
 179 idm_conn_event(idm_conn_t *ic, idm_conn_event_t event, uintptr_t event_info)
 180 {
 181         mutex_enter(&ic->ic_state_mutex);
 182         idm_conn_event_locked(ic, event, event_info, CT_NONE);
 183         mutex_exit(&ic->ic_state_mutex);
 184 }
 185 
 186 
 187 idm_status_t
 188 idm_conn_reinstate_event(idm_conn_t *old_ic, idm_conn_t *new_ic)
 189 {
 190         int result;
 191 
 192         mutex_enter(&old_ic->ic_state_mutex);
 193         if (((old_ic->ic_conn_type == CONN_TYPE_INI) &&
 194             (old_ic->ic_state != CS_S8_CLEANUP)) ||


 476         case CE_TRANSPORT_FAIL:
 477         case CE_CONNECT_FAIL:
 478         case CE_LOGOUT_OTHER_CONN_RCV:
 479         case CE_TX_PROTOCOL_ERROR:
 480         case CE_RX_PROTOCOL_ERROR:
 481                 /* T2 */
 482                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 483                 break;
 484         default:
 485                 ASSERT(0);
 486                 /*NOTREACHED*/
 487         }
 488 }
 489 
 490 
 491 static void
 492 idm_login_timeout(void *arg)
 493 {
 494         idm_conn_t *ic = arg;
 495 
 496         ic->ic_state_timeout = 0;
 497         idm_conn_event(ic, CE_LOGIN_TIMEOUT, NULL);
 498 }
 499 
 500 static void
 501 idm_state_s3_xpt_up(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 502 {
 503         switch (event_ctx->iec_event) {
 504         case CE_LOGIN_RCV:
 505                 /* T4 */
 506                 /* Keep login timeout active through S3 and into S4 */
 507                 idm_initial_login_actions(ic, event_ctx);
 508                 idm_update_state(ic, CS_S4_IN_LOGIN, event_ctx);
 509                 break;
 510         case CE_LOGIN_TIMEOUT:
 511                 /*
 512                  * Don't need to cancel login timer since the timer is
 513                  * presumed to be the source of this event.
 514                  */
 515                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 516                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 517                 break;
 518         case CE_CONNECT_REJECT:
 519                 /*
 520                  * Iscsit doesn't want to hear from us again in this case.
 521                  * Since it rejected the connection it doesn't have a
 522                  * connection context to handle additional notifications.
 523                  * IDM needs to just clean things up on its own.
 524                  */
 525                 IDM_SM_TIMER_CLEAR(ic);
 526                 idm_update_state(ic, CS_S9A_REJECTED, event_ctx);
 527                 break;
 528         case CE_CONNECT_FAIL:
 529         case CE_TRANSPORT_FAIL:
 530         case CE_LOGOUT_OTHER_CONN_SND:
 531                 /* T6 */
 532                 IDM_SM_TIMER_CLEAR(ic);
 533                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 534                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 535                 break;
 536         case CE_TX_PROTOCOL_ERROR:
 537         case CE_RX_PROTOCOL_ERROR:
 538                 /* Don't care */
 539                 break;
 540         default:
 541                 ASSERT(0);
 542                 /*NOTREACHED*/
 543         }
 544 }
 545 
 546 static void
 547 idm_state_s4_in_login(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 548 {
 549         idm_pdu_t *pdu;
 550 
 551         /*
 552          * Login timer should no longer be active after leaving this
 553          * state.
 554          */
 555         switch (event_ctx->iec_event) {
 556         case CE_LOGIN_SUCCESS_RCV:
 557         case CE_LOGIN_SUCCESS_SND:
 558                 ASSERT(ic->ic_client_callback == NULL);
 559 
 560                 IDM_SM_TIMER_CLEAR(ic);
 561                 idm_login_success_actions(ic, event_ctx);
 562                 if (ic->ic_rdma_extensions) {
 563                         /* T19 */
 564                         idm_update_state(ic, CS_S12_ENABLE_DM, event_ctx);
 565                 } else {
 566                         /* T5 */
 567                         idm_update_state(ic, CS_S5_LOGGED_IN, event_ctx);
 568                 }
 569                 break;
 570         case CE_LOGIN_TIMEOUT:
 571                 /* T7 */
 572                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 573                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 574                 break;
 575         case CE_LOGIN_FAIL_SND:
 576                 /*
 577                  * Allow the logout response pdu to be sent and defer
 578                  * the state machine cleanup until the completion callback.
 579                  * Only 1 level or callback interposition is allowed.
 580                  */
 581                 IDM_SM_TIMER_CLEAR(ic);
 582                 pdu = (idm_pdu_t *)event_ctx->iec_info;
 583                 ASSERT(ic->ic_client_callback == NULL);
 584                 ic->ic_client_callback = pdu->isp_callback;
 585                 pdu->isp_callback =
 586                     idm_state_s9b_wait_snd_done_cb;
 587                 idm_update_state(ic, CS_S9B_WAIT_SND_DONE,
 588                     event_ctx);
 589                 break;
 590         case CE_LOGIN_FAIL_RCV:
 591                 ASSERT(ic->ic_client_callback == NULL);
 592                 /*
 593                  * Need to deliver this PDU to the initiator now because after
 594                  * we update the state to CS_S9_INIT_ERROR the initiator will
 595                  * no longer be in an appropriate state.
 596                  */
 597                 event_ctx->iec_pdu_forwarded = B_TRUE;
 598                 pdu = (idm_pdu_t *)event_ctx->iec_info;
 599                 idm_pdu_rx_forward(ic, pdu);
 600                 /* FALLTHROUGH */
 601         case CE_TRANSPORT_FAIL:
 602         case CE_LOGOUT_OTHER_CONN_SND:
 603         case CE_LOGOUT_OTHER_CONN_RCV:
 604                 /* T7 */
 605                 IDM_SM_TIMER_CLEAR(ic);
 606                 (void) idm_notify_client(ic, CN_LOGIN_FAIL, NULL);
 607                 idm_update_state(ic, CS_S9_INIT_ERROR, event_ctx);
 608                 break;
 609         case CE_LOGOUT_SESSION_SUCCESS:
 610                 /*
 611                  * T8
 612                  * A session reinstatement request can be received while a
 613                  * session is active and a login is in process. The iSCSI
 614                  * connections are shut down by a CE_LOGOUT_SESSION_SUCCESS
 615                  * event sent from the session to the IDM layer.
 616                  */
 617                 IDM_SM_TIMER_CLEAR(ic);
 618                 if (IDM_CONN_ISTGT(ic)) {
 619                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
 620                 } else {
 621                         ic->ic_transport_ops->it_ini_conn_disconnect(ic);
 622                 }
 623                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
 624                 break;
 625 
 626         case CE_LOGIN_SND:
 627                 ASSERT(ic->ic_client_callback == NULL);
 628                 /*
 629                  * Initiator connections will see initial login PDU
 630                  * in this state.  Target connections see initial
 631                  * login PDU in "xpt up" state.
 632                  */
 633                 mutex_enter(&ic->ic_state_mutex);
 634                 if (!(ic->ic_state_flags & CF_INITIAL_LOGIN)) {
 635                         idm_initial_login_actions(ic, event_ctx);
 636                 }
 637                 mutex_exit(&ic->ic_state_mutex);


 813                 idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
 814                 break;
 815         case CE_TX_PROTOCOL_ERROR:
 816         case CE_RX_PROTOCOL_ERROR:
 817         case CE_MISC_TX:
 818         case CE_MISC_RX:
 819         case CE_LOGIN_TIMEOUT:
 820                 /* Don't care */
 821                 break;
 822         default:
 823                 ASSERT(0);
 824         }
 825 }
 826 
 827 
 828 static void
 829 idm_logout_req_timeout(void *arg)
 830 {
 831         idm_conn_t *ic = arg;
 832 
 833         ic->ic_state_timeout = 0;
 834         idm_conn_event(ic, CE_LOGOUT_TIMEOUT, NULL);
 835 }
 836 
 837 static void
 838 idm_state_s7_logout_req(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 839 {
 840         /* Must cancel logout timer before leaving this state */
 841         switch (event_ctx->iec_event) {
 842         case CE_LOGOUT_THIS_CONN_RCV:
 843         case CE_LOGOUT_THIS_CONN_SND:
 844         case CE_LOGOUT_OTHER_CONN_RCV:
 845         case CE_LOGOUT_OTHER_CONN_SND:
 846                 /* T10 */
 847                 if (IDM_CONN_ISTGT(ic)) {
 848                         IDM_SM_TIMER_CLEAR(ic);
 849                 }
 850                 idm_ffp_disable(ic, FD_CONN_LOGOUT); /* Explicit logout */
 851                 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
 852                 break;
 853         case CE_LOGOUT_SESSION_RCV:
 854         case CE_LOGOUT_SESSION_SND:
 855                 /* T10 */
 856                 if (IDM_CONN_ISTGT(ic)) {
 857                         IDM_SM_TIMER_CLEAR(ic);
 858                 }
 859                 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
 860                 idm_update_state(ic, CS_S6_IN_LOGOUT, event_ctx);
 861                 break;
 862         case CE_ASYNC_LOGOUT_RCV:
 863         case CE_ASYNC_LOGOUT_SND:
 864                 /* T12 Do nothing */
 865                 break;
 866         case CE_TRANSPORT_FAIL:
 867         case CE_ASYNC_DROP_CONN_RCV:
 868         case CE_ASYNC_DROP_CONN_SND:
 869         case CE_ASYNC_DROP_ALL_CONN_RCV:
 870         case CE_ASYNC_DROP_ALL_CONN_SND:
 871                 /* T16 */
 872                 if (IDM_CONN_ISTGT(ic)) {
 873                         IDM_SM_TIMER_CLEAR(ic);
 874                 }
 875                 /* FALLTHROUGH */
 876         case CE_LOGOUT_TIMEOUT:
 877                 idm_ffp_disable(ic, FD_CONN_FAIL); /* Implicit logout */
 878                 idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
 879                 break;
 880         case CE_LOGOUT_SESSION_SUCCESS:
 881                 /* T18 */
 882                 if (IDM_CONN_ISTGT(ic)) {
 883                         IDM_SM_TIMER_CLEAR(ic);
 884                 }
 885                 idm_ffp_disable(ic, FD_SESS_LOGOUT); /* Explicit logout */
 886 
 887                 /* Close connection (if it's not already closed) */
 888                 if (IDM_CONN_ISTGT(ic)) {
 889                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
 890                 } else {
 891                         ic->ic_transport_ops->it_ini_conn_disconnect(ic);
 892                 }
 893 
 894                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
 895                 break;
 896         case CE_TX_PROTOCOL_ERROR:
 897         case CE_RX_PROTOCOL_ERROR:
 898         case CE_MISC_TX:
 899         case CE_MISC_RX:
 900         case CE_LOGIN_TIMEOUT:
 901                 /* Don't care */
 902                 break;
 903         default:
 904                 ASSERT(0);
 905         }
 906 }
 907 
 908 
 909 static void
 910 idm_cleanup_timeout(void *arg)
 911 {
 912         idm_conn_t *ic = arg;
 913 
 914         ic->ic_state_timeout = 0;
 915         idm_conn_event(ic, CE_CLEANUP_TIMEOUT, NULL);
 916 }
 917 
 918 static void
 919 idm_state_s8_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
 920 {
 921         idm_pdu_t *pdu;
 922 
 923         /*
 924          * Need to cancel the cleanup timeout before leaving this state
 925          * if it hasn't already fired.
 926          */
 927         switch (event_ctx->iec_event) {
 928         case CE_LOGOUT_SUCCESS_RCV:
 929         case CE_LOGOUT_SUCCESS_SND:
 930         case CE_LOGOUT_SESSION_SUCCESS:
 931                 IDM_SM_TIMER_CLEAR(ic);
 932                 /*FALLTHROUGH*/
 933         case CE_CLEANUP_TIMEOUT:
 934                 /* M1 */
 935                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
 936                 break;
 937         case CE_LOGOUT_OTHER_CONN_RCV:
 938         case CE_LOGOUT_OTHER_CONN_SND:
 939                 /* M2 */
 940                 idm_update_state(ic, CS_S10_IN_CLEANUP, event_ctx);
 941                 break;
 942         case CE_LOGOUT_SUCCESS_SND_DONE:
 943         case CE_LOGOUT_FAIL_SND_DONE:
 944                 pdu = (idm_pdu_t *)event_ctx->iec_info;
 945                 /* restore client callback */
 946                 pdu->isp_callback =  ic->ic_client_callback;
 947                 ic->ic_client_callback = NULL;
 948                 idm_pdu_complete(pdu, pdu->isp_status);
 949                 break;
 950         case CE_LOGOUT_SESSION_RCV:
 951         case CE_LOGOUT_SESSION_SND:


1020 
1021 
1022 
1023 static void
1024 idm_state_s10_in_cleanup(idm_conn_t *ic, idm_conn_event_ctx_t *event_ctx)
1025 {
1026         idm_pdu_t *pdu;
1027 
1028         /*
1029          * Need to cancel the cleanup timeout before leaving this state
1030          * if it hasn't already fired.
1031          */
1032         switch (event_ctx->iec_event) {
1033         case CE_LOGOUT_FAIL_RCV:
1034         case CE_LOGOUT_FAIL_SND:
1035                 idm_update_state(ic, CS_S8_CLEANUP, event_ctx);
1036                 break;
1037         case CE_LOGOUT_SUCCESS_SND:
1038         case CE_LOGOUT_SUCCESS_RCV:
1039         case CE_LOGOUT_SESSION_SUCCESS:
1040                 IDM_SM_TIMER_CLEAR(ic);
1041                 /*FALLTHROUGH*/
1042         case CE_CLEANUP_TIMEOUT:
1043                 idm_update_state(ic, CS_S11_COMPLETE, event_ctx);
1044                 break;
1045         case CE_LOGOUT_SUCCESS_SND_DONE:
1046         case CE_LOGOUT_FAIL_SND_DONE:
1047                 pdu = (idm_pdu_t *)event_ctx->iec_info;
1048                 /* restore client callback */
1049                 pdu->isp_callback =  ic->ic_client_callback;
1050                 ic->ic_client_callback = NULL;
1051                 idm_pdu_complete(pdu, pdu->isp_status);
1052                 break;
1053         case CE_TX_PROTOCOL_ERROR:
1054         case CE_RX_PROTOCOL_ERROR:
1055         case CE_MISC_TX:
1056         case CE_MISC_RX:
1057         case CE_LOGIN_TIMEOUT:
1058         case CE_LOGOUT_TIMEOUT:
1059                 /* Don't care */
1060                 break;


1176                  * Finish any connection related setup including
1177                  * waking up the idm_tgt_conn_accept thread.
1178                  * and starting the login timer.  If the function
1179                  * fails then we return to "free" state.
1180                  */
1181                 if ((rc = idm_tgt_conn_finish(ic)) != IDM_STATUS_SUCCESS) {
1182                         switch (rc) {
1183                         case IDM_STATUS_REJECT:
1184                                 idm_conn_event(ic, CE_CONNECT_REJECT, NULL);
1185                                 break;
1186                         default:
1187                                 idm_conn_event(ic, CE_CONNECT_FAIL, NULL);
1188                                 break;
1189                         }
1190                 }
1191 
1192                 /*
1193                  * First login received will cause a transition to
1194                  * CS_S4_IN_LOGIN.  Start login timer.
1195                  */
1196                 IDM_SM_TIMER_CHECK(ic);
1197                 ic->ic_state_timeout = timeout(idm_login_timeout, ic,
1198                     drv_usectohz(IDM_LOGIN_SECONDS*1000000));
1199                 break;
1200         case CS_S4_IN_LOGIN:
1201                 if (ic->ic_conn_type == CONN_TYPE_INI) {
1202                         (void) idm_notify_client(ic, CN_READY_FOR_LOGIN, NULL);
1203                         mutex_enter(&ic->ic_state_mutex);
1204                         ic->ic_state_flags |= CF_LOGIN_READY;
1205                         cv_signal(&ic->ic_state_cv);
1206                         mutex_exit(&ic->ic_state_mutex);
1207                 }
1208                 break;
1209         case CS_S5_LOGGED_IN:
1210                 ASSERT(!ic->ic_ffp);
1211                 /*
1212                  * IDM can go to FFP before the initiator but it
1213                  * needs to go to FFP after the target (IDM target should
1214                  * go to FFP after notify_ack).
1215                  */
1216                 idm_status = idm_ffp_enable(ic);
1217                 if (idm_status != IDM_STATUS_SUCCESS) {
1218                         idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
1219                 }
1220 
1221                 if (ic->ic_reinstate_conn) {
1222                         /* Connection reinstatement is complete */
1223                         idm_conn_event(ic->ic_reinstate_conn,
1224                             CE_CONN_REINSTATE_SUCCESS, NULL);
1225                 }
1226                 break;
1227         case CS_S6_IN_LOGOUT:
1228                 break;
1229         case CS_S7_LOGOUT_REQ:
1230                 /* Start logout timer for target connections */
1231                 if (IDM_CONN_ISTGT(ic)) {
1232                         IDM_SM_TIMER_CHECK(ic);
1233                         ic->ic_state_timeout = timeout(idm_logout_req_timeout,
1234                             ic, drv_usectohz(IDM_LOGOUT_SECONDS*1000000));
1235                 }
1236                 break;
1237         case CS_S8_CLEANUP:
1238                 /* Close connection (if it's not already closed) */
1239                 if (IDM_CONN_ISTGT(ic)) {
1240                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1241                 } else {
1242                         ic->ic_transport_ops->it_ini_conn_disconnect(ic);
1243                 }
1244 
1245                 /* Stop executing active tasks */
1246                 idm_task_abort(ic, NULL, AT_INTERNAL_SUSPEND);
1247 
1248                 /* Start logout timer */
1249                 IDM_SM_TIMER_CHECK(ic);
1250                 ic->ic_state_timeout = timeout(idm_cleanup_timeout, ic,
1251                     drv_usectohz(IDM_CLEANUP_SECONDS*1000000));
1252                 break;
1253         case CS_S10_IN_CLEANUP:
1254                 break;
1255         case CS_S9A_REJECTED:
1256                 /*
1257                  * We never finished establishing the connection so no
1258                  * disconnect.  No client notifications because the client
1259                  * rejected the connection.
1260                  */
1261                 idm_refcnt_async_wait_ref(&ic->ic_refcnt,
1262                     &idm_conn_reject_unref);
1263                 break;
1264         case CS_S9B_WAIT_SND_DONE:
1265                 break;
1266         case CS_S9_INIT_ERROR:
1267                 if (IDM_CONN_ISTGT(ic)) {
1268                         ic->ic_transport_ops->it_tgt_conn_disconnect(ic);
1269                 } else {