1 /* 2 * CDDL HEADER START 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 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/conf.h> 28 #include <sys/file.h> 29 #include <sys/ddi.h> 30 #include <sys/sunddi.h> 31 #include <sys/cpuvar.h> 32 #include <sys/sdt.h> 33 34 #include <sys/socket.h> 35 #include <sys/strsubr.h> 36 #include <sys/socketvar.h> 37 #include <sys/sysmacros.h> 38 39 #include <sys/idm/idm.h> 40 #include <sys/idm/idm_so.h> 41 #include <hd_crc.h> 42 43 extern idm_transport_t idm_transport_list[]; 44 /* 45 * -1 - uninitialized 46 * 0 - applicable 47 * others - NA 48 */ 49 static int iscsi_crc32_hd = -1; 50 51 void 52 idm_pdu_rx(idm_conn_t *ic, idm_pdu_t *pdu) 53 { 54 iscsi_async_evt_hdr_t *async_evt; 55 56 /* 57 * If we are in full-featured mode then route SCSI-related 58 * commands to the appropriate function vector 59 */ 60 ic->ic_timestamp = ddi_get_lbolt(); 61 mutex_enter(&ic->ic_state_mutex); 62 if (ic->ic_ffp && ic->ic_pdu_events == 0) { 63 mutex_exit(&ic->ic_state_mutex); 64 65 if (idm_pdu_rx_forward_ffp(ic, pdu) == B_TRUE) { 66 /* Forwarded SCSI-related commands */ 67 return; 68 } 69 mutex_enter(&ic->ic_state_mutex); 70 } 71 72 /* 73 * If we get here with a SCSI-related PDU then we are not in 74 * full-feature mode and the PDU is a protocol error (SCSI command 75 * PDU's may sometimes be an exception, see below). All 76 * non-SCSI PDU's get treated them the same regardless of whether 77 * we are in full-feature mode. 78 * 79 * Look at the opcode and in some cases the PDU status and 80 * determine the appropriate event to send to the connection 81 * state machine. Generate the event, passing the PDU as data. 82 * If the current connection state allows reception of the event 83 * the PDU will be submitted to the IDM client for processing, 84 * otherwise the PDU will be dropped. 85 */ 86 switch (IDM_PDU_OPCODE(pdu)) { 87 case ISCSI_OP_LOGIN_CMD: 88 DTRACE_ISCSI_2(login__command, idm_conn_t *, ic, 89 iscsi_login_hdr_t *, (iscsi_login_hdr_t *)pdu->isp_hdr); 90 idm_conn_rx_pdu_event(ic, CE_LOGIN_RCV, (uintptr_t)pdu); 91 break; 92 case ISCSI_OP_LOGIN_RSP: 93 idm_parse_login_rsp(ic, pdu, /* RX */ B_TRUE); 94 break; 95 case ISCSI_OP_LOGOUT_CMD: 96 DTRACE_ISCSI_2(logout__command, idm_conn_t *, ic, 97 iscsi_logout_hdr_t *, 98 (iscsi_logout_hdr_t *)pdu->isp_hdr); 99 idm_parse_logout_req(ic, pdu, /* RX */ B_TRUE); 100 break; 101 case ISCSI_OP_LOGOUT_RSP: 102 idm_parse_logout_rsp(ic, pdu, /* RX */ B_TRUE); 103 break; 104 case ISCSI_OP_ASYNC_EVENT: 105 async_evt = (iscsi_async_evt_hdr_t *)pdu->isp_hdr; 106 switch (async_evt->async_event) { 107 case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT: 108 idm_conn_rx_pdu_event(ic, CE_ASYNC_LOGOUT_RCV, 109 (uintptr_t)pdu); 110 break; 111 case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION: 112 idm_conn_rx_pdu_event(ic, CE_ASYNC_DROP_CONN_RCV, 113 (uintptr_t)pdu); 114 break; 115 case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS: 116 idm_conn_rx_pdu_event(ic, CE_ASYNC_DROP_ALL_CONN_RCV, 117 (uintptr_t)pdu); 118 break; 119 case ISCSI_ASYNC_EVENT_SCSI_EVENT: 120 case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION: 121 default: 122 idm_conn_rx_pdu_event(ic, CE_MISC_RX, 123 (uintptr_t)pdu); 124 break; 125 } 126 break; 127 case ISCSI_OP_SCSI_CMD: 128 /* 129 * Consider this scenario: We are a target connection 130 * in "in login" state and a "login success sent" event has 131 * been generated but not yet handled. Since we've sent 132 * the login response but we haven't actually transitioned 133 * to FFP mode we might conceivably receive a SCSI command 134 * from the initiator before we are ready. We are actually 135 * in FFP we just don't know it yet -- to address this we 136 * can generate an event corresponding to the SCSI command. 137 * At the point when the event is handled by the state 138 * machine the login request will have been handled and we 139 * should be in FFP. If we are not in FFP by that time 140 * we can reject the SCSI command with a protocol error. 141 * 142 * This scenario only applies to the target. 143 * 144 * Handle dtrace probe in iscsit so we can find all the 145 * pieces of the CDB 146 */ 147 idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu); 148 break; 149 case ISCSI_OP_SCSI_DATA: 150 DTRACE_ISCSI_2(data__receive, idm_conn_t *, ic, 151 iscsi_data_hdr_t *, 152 (iscsi_data_hdr_t *)pdu->isp_hdr); 153 idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu); 154 break; 155 case ISCSI_OP_SCSI_TASK_MGT_MSG: 156 DTRACE_ISCSI_2(task__command, idm_conn_t *, ic, 157 iscsi_scsi_task_mgt_hdr_t *, 158 (iscsi_scsi_task_mgt_hdr_t *)pdu->isp_hdr); 159 idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu); 160 break; 161 case ISCSI_OP_NOOP_OUT: 162 DTRACE_ISCSI_2(nop__receive, idm_conn_t *, ic, 163 iscsi_nop_out_hdr_t *, 164 (iscsi_nop_out_hdr_t *)pdu->isp_hdr); 165 idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu); 166 break; 167 case ISCSI_OP_TEXT_CMD: 168 DTRACE_ISCSI_2(text__command, idm_conn_t *, ic, 169 iscsi_text_hdr_t *, 170 (iscsi_text_hdr_t *)pdu->isp_hdr); 171 idm_conn_rx_pdu_event(ic, CE_MISC_RX, (uintptr_t)pdu); 172 break; 173 /* Initiator PDU's */ 174 case ISCSI_OP_SCSI_DATA_RSP: 175 case ISCSI_OP_RTT_RSP: 176 case ISCSI_OP_SNACK_CMD: 177 case ISCSI_OP_NOOP_IN: 178 case ISCSI_OP_TEXT_RSP: 179 case ISCSI_OP_REJECT_MSG: 180 case ISCSI_OP_SCSI_TASK_MGT_RSP: 181 /* Validate received PDU against current state */ 182 idm_conn_rx_pdu_event(ic, CE_MISC_RX, 183 (uintptr_t)pdu); 184 break; 185 } 186 mutex_exit(&ic->ic_state_mutex); 187 } 188 189 void 190 idm_pdu_tx_forward(idm_conn_t *ic, idm_pdu_t *pdu) 191 { 192 (*ic->ic_transport_ops->it_tx_pdu)(ic, pdu); 193 } 194 195 boolean_t 196 idm_pdu_rx_forward_ffp(idm_conn_t *ic, idm_pdu_t *pdu) 197 { 198 /* 199 * If this is an FFP request, call the appropriate handler 200 * and return B_TRUE, otherwise return B_FALSE. 201 */ 202 switch (IDM_PDU_OPCODE(pdu)) { 203 case ISCSI_OP_SCSI_CMD: 204 (*ic->ic_conn_ops.icb_rx_scsi_cmd)(ic, pdu); 205 return (B_TRUE); 206 case ISCSI_OP_SCSI_DATA: 207 DTRACE_ISCSI_2(data__receive, idm_conn_t *, ic, 208 iscsi_data_hdr_t *, 209 (iscsi_data_hdr_t *)pdu->isp_hdr); 210 (*ic->ic_transport_ops->it_rx_dataout)(ic, pdu); 211 return (B_TRUE); 212 case ISCSI_OP_SCSI_TASK_MGT_MSG: 213 DTRACE_ISCSI_2(task__command, idm_conn_t *, ic, 214 iscsi_scsi_task_mgt_hdr_t *, 215 (iscsi_scsi_task_mgt_hdr_t *)pdu->isp_hdr); 216 (*ic->ic_conn_ops.icb_rx_misc)(ic, pdu); 217 return (B_TRUE); 218 case ISCSI_OP_NOOP_OUT: 219 DTRACE_ISCSI_2(nop__receive, idm_conn_t *, ic, 220 iscsi_nop_out_hdr_t *, 221 (iscsi_nop_out_hdr_t *)pdu->isp_hdr); 222 (*ic->ic_conn_ops.icb_rx_misc)(ic, pdu); 223 return (B_TRUE); 224 case ISCSI_OP_TEXT_CMD: 225 DTRACE_ISCSI_2(text__command, idm_conn_t *, ic, 226 iscsi_text_hdr_t *, 227 (iscsi_text_hdr_t *)pdu->isp_hdr); 228 (*ic->ic_conn_ops.icb_rx_misc)(ic, pdu); 229 return (B_TRUE); 230 /* Initiator only */ 231 case ISCSI_OP_SCSI_RSP: 232 (*ic->ic_conn_ops.icb_rx_scsi_rsp)(ic, pdu); 233 return (B_TRUE); 234 case ISCSI_OP_SCSI_DATA_RSP: 235 (*ic->ic_transport_ops->it_rx_datain)(ic, pdu); 236 return (B_TRUE); 237 case ISCSI_OP_RTT_RSP: 238 (*ic->ic_transport_ops->it_rx_rtt)(ic, pdu); 239 return (B_TRUE); 240 case ISCSI_OP_SCSI_TASK_MGT_RSP: 241 case ISCSI_OP_TEXT_RSP: 242 case ISCSI_OP_NOOP_IN: 243 (*ic->ic_conn_ops.icb_rx_misc)(ic, pdu); 244 return (B_TRUE); 245 default: 246 return (B_FALSE); 247 } 248 /*NOTREACHED*/ 249 } 250 251 void 252 idm_pdu_rx_forward(idm_conn_t *ic, idm_pdu_t *pdu) 253 { 254 /* 255 * Some PDU's specific to FFP get special handling. This function 256 * will normally never be called in FFP with an FFP PDU since this 257 * is a slow path but in can happen on the target side during 258 * the transition to FFP. We primarily call 259 * idm_pdu_rx_forward_ffp here to avoid code duplication. 260 */ 261 if (idm_pdu_rx_forward_ffp(ic, pdu) == B_FALSE) { 262 /* 263 * Non-FFP PDU, use generic RC handler 264 */ 265 (*ic->ic_conn_ops.icb_rx_misc)(ic, pdu); 266 } 267 } 268 269 void 270 idm_parse_login_rsp(idm_conn_t *ic, idm_pdu_t *login_rsp_pdu, boolean_t rx) 271 { 272 iscsi_login_rsp_hdr_t *login_rsp = 273 (iscsi_login_rsp_hdr_t *)login_rsp_pdu->isp_hdr; 274 idm_conn_event_t new_event; 275 276 if (login_rsp->status_class == ISCSI_STATUS_CLASS_SUCCESS) { 277 if (!(login_rsp->flags & ISCSI_FLAG_LOGIN_CONTINUE) && 278 (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) && 279 (ISCSI_LOGIN_NEXT_STAGE(login_rsp->flags) == 280 ISCSI_FULL_FEATURE_PHASE)) { 281 new_event = (rx ? CE_LOGIN_SUCCESS_RCV : 282 CE_LOGIN_SUCCESS_SND); 283 } else { 284 new_event = (rx ? CE_MISC_RX : CE_MISC_TX); 285 } 286 } else { 287 new_event = (rx ? CE_LOGIN_FAIL_RCV : CE_LOGIN_FAIL_SND); 288 } 289 290 if (rx) { 291 idm_conn_rx_pdu_event(ic, new_event, (uintptr_t)login_rsp_pdu); 292 } else { 293 idm_conn_tx_pdu_event(ic, new_event, (uintptr_t)login_rsp_pdu); 294 } 295 } 296 297 298 void 299 idm_parse_logout_req(idm_conn_t *ic, idm_pdu_t *logout_req_pdu, boolean_t rx) 300 { 301 iscsi_logout_hdr_t *logout_req = 302 (iscsi_logout_hdr_t *)logout_req_pdu->isp_hdr; 303 idm_conn_event_t new_event; 304 uint8_t reason = 305 (logout_req->flags & ISCSI_FLAG_LOGOUT_REASON_MASK); 306 307 /* 308 * For a normal logout (close connection or close session) IDM 309 * will terminate processing of all tasks completing the tasks 310 * back to the client with a status indicating the connection 311 * was logged out. These tasks do not get completed. 312 * 313 * For a "close connection for recovery logout) IDM suspends 314 * processing of all tasks and completes them back to the client 315 * with a status indicating connection was logged out for 316 * recovery. Both initiator and target hang onto these tasks. 317 * When we add ERL2 support IDM will need to provide mechanisms 318 * to change the task and buffer associations to a new connection. 319 * 320 * This code doesn't address the possibility of MC/S. We'll 321 * need to decide how the separate connections get handled 322 * in that case. One simple option is to make the client 323 * generate the events for the other connections. 324 */ 325 if (reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) { 326 new_event = 327 (rx ? CE_LOGOUT_SESSION_RCV : CE_LOGOUT_SESSION_SND); 328 } else if ((reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) || 329 (reason == ISCSI_LOGOUT_REASON_RECOVERY)) { 330 /* Check logout CID against this connection's CID */ 331 if (ntohs(logout_req->cid) == ic->ic_login_cid) { 332 /* Logout is for this connection */ 333 new_event = (rx ? CE_LOGOUT_THIS_CONN_RCV : 334 CE_LOGOUT_THIS_CONN_SND); 335 } else { 336 /* 337 * Logout affects another connection. This is not 338 * a relevant event for this connection so we'll 339 * just treat it as a normal PDU event. Client 340 * will need to lookup the other connection and 341 * generate the event. 342 */ 343 new_event = (rx ? CE_MISC_RX : CE_MISC_TX); 344 } 345 } else { 346 /* Invalid reason code */ 347 new_event = (rx ? CE_RX_PROTOCOL_ERROR : CE_TX_PROTOCOL_ERROR); 348 } 349 350 if (rx) { 351 idm_conn_rx_pdu_event(ic, new_event, (uintptr_t)logout_req_pdu); 352 } else { 353 idm_conn_tx_pdu_event(ic, new_event, (uintptr_t)logout_req_pdu); 354 } 355 } 356 357 358 359 void 360 idm_parse_logout_rsp(idm_conn_t *ic, idm_pdu_t *logout_rsp_pdu, boolean_t rx) 361 { 362 idm_conn_event_t new_event; 363 iscsi_logout_rsp_hdr_t *logout_rsp = 364 (iscsi_logout_rsp_hdr_t *)logout_rsp_pdu->isp_hdr; 365 366 if (logout_rsp->response == ISCSI_STATUS_CLASS_SUCCESS) { 367 new_event = rx ? CE_LOGOUT_SUCCESS_RCV : CE_LOGOUT_SUCCESS_SND; 368 } else { 369 new_event = rx ? CE_LOGOUT_FAIL_RCV : CE_LOGOUT_FAIL_SND; 370 } 371 372 if (rx) { 373 idm_conn_rx_pdu_event(ic, new_event, (uintptr_t)logout_rsp_pdu); 374 } else { 375 idm_conn_tx_pdu_event(ic, new_event, (uintptr_t)logout_rsp_pdu); 376 } 377 } 378 379 /* 380 * idm_svc_conn_create() 381 * Transport-agnostic service connection creation, invoked from the transport 382 * layer. 383 */ 384 idm_status_t 385 idm_svc_conn_create(idm_svc_t *is, idm_transport_type_t tt, 386 idm_conn_t **ic_result) 387 { 388 idm_conn_t *ic; 389 idm_status_t rc; 390 391 /* 392 * Skip some work if we can already tell we are going offline. 393 * Otherwise we will destroy this connection later as part of 394 * shutting down the svc. 395 */ 396 mutex_enter(&is->is_mutex); 397 if (!is->is_online) { 398 mutex_exit(&is->is_mutex); 399 return (IDM_STATUS_FAIL); 400 } 401 mutex_exit(&is->is_mutex); 402 403 ic = idm_conn_create_common(CONN_TYPE_TGT, tt, 404 &is->is_svc_req.sr_conn_ops); 405 if (ic == NULL) { 406 return (IDM_STATUS_FAIL); 407 } 408 ic->ic_svc_binding = is; 409 410 /* 411 * Prepare connection state machine 412 */ 413 if ((rc = idm_conn_sm_init(ic)) != 0) { 414 idm_conn_destroy_common(ic); 415 return (rc); 416 } 417 418 419 *ic_result = ic; 420 421 mutex_enter(&idm.idm_global_mutex); 422 list_insert_tail(&idm.idm_tgt_conn_list, ic); 423 idm.idm_tgt_conn_count++; 424 mutex_exit(&idm.idm_global_mutex); 425 426 return (IDM_STATUS_SUCCESS); 427 } 428 429 void 430 idm_svc_conn_destroy(idm_conn_t *ic) 431 { 432 mutex_enter(&idm.idm_global_mutex); 433 list_remove(&idm.idm_tgt_conn_list, ic); 434 idm.idm_tgt_conn_count--; 435 mutex_exit(&idm.idm_global_mutex); 436 437 if (ic->ic_transport_private != NULL) { 438 ic->ic_transport_ops->it_tgt_conn_destroy(ic); 439 } 440 idm_conn_destroy_common(ic); 441 } 442 443 /* 444 * idm_conn_create_common() 445 * 446 * Allocate and initialize IDM connection context 447 */ 448 idm_conn_t * 449 idm_conn_create_common(idm_conn_type_t conn_type, idm_transport_type_t tt, 450 idm_conn_ops_t *conn_ops) 451 { 452 idm_conn_t *ic; 453 idm_transport_t *it; 454 idm_transport_type_t type; 455 456 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 457 it = &idm_transport_list[type]; 458 459 if ((it->it_ops != NULL) && (it->it_type == tt)) 460 break; 461 } 462 ASSERT(it->it_type == tt); 463 if (it->it_type != tt) 464 return (NULL); 465 466 ic = kmem_zalloc(sizeof (idm_conn_t), KM_SLEEP); 467 468 /* Initialize data */ 469 ic->ic_target_name[0] = '\0'; 470 ic->ic_initiator_name[0] = '\0'; 471 ic->ic_isid[0] = '\0'; 472 ic->ic_tsih[0] = '\0'; 473 ic->ic_conn_type = conn_type; 474 ic->ic_conn_ops = *conn_ops; 475 ic->ic_transport_ops = it->it_ops; 476 ic->ic_transport_type = tt; 477 ic->ic_transport_private = NULL; /* Set by transport service */ 478 ic->ic_internal_cid = idm_cid_alloc(); 479 if (ic->ic_internal_cid == 0) { 480 kmem_free(ic, sizeof (idm_conn_t)); 481 return (NULL); 482 } 483 mutex_init(&ic->ic_mutex, NULL, MUTEX_DEFAULT, NULL); 484 cv_init(&ic->ic_cv, NULL, CV_DEFAULT, NULL); 485 idm_refcnt_init(&ic->ic_refcnt, ic); 486 487 return (ic); 488 } 489 490 void 491 idm_conn_destroy_common(idm_conn_t *ic) 492 { 493 idm_conn_sm_fini(ic); 494 idm_refcnt_destroy(&ic->ic_refcnt); 495 cv_destroy(&ic->ic_cv); 496 mutex_destroy(&ic->ic_mutex); 497 idm_cid_free(ic->ic_internal_cid); 498 499 kmem_free(ic, sizeof (idm_conn_t)); 500 } 501 502 /* 503 * Invoked from the SM as a result of client's invocation of 504 * idm_ini_conn_connect() 505 */ 506 idm_status_t 507 idm_ini_conn_finish(idm_conn_t *ic) 508 { 509 /* invoke transport-specific connection */ 510 return (ic->ic_transport_ops->it_ini_conn_connect(ic)); 511 } 512 513 idm_status_t 514 idm_tgt_conn_finish(idm_conn_t *ic) 515 { 516 idm_status_t rc; 517 518 rc = idm_notify_client(ic, CN_CONNECT_ACCEPT, NULL); 519 if (rc != IDM_STATUS_SUCCESS) { 520 return (IDM_STATUS_REJECT); 521 } 522 523 /* Target client is ready to receive a login, start connection */ 524 return (ic->ic_transport_ops->it_tgt_conn_connect(ic)); 525 } 526 527 idm_transport_t * 528 idm_transport_lookup(idm_conn_req_t *cr) 529 { 530 idm_transport_type_t type; 531 idm_transport_t *it; 532 idm_transport_caps_t caps; 533 534 /* 535 * Make sure all available transports are setup. We call this now 536 * instead of at initialization time in case IB has become available 537 * since we started (hotplug, etc). 538 */ 539 idm_transport_setup(cr->cr_li, cr->cr_boot_conn); 540 541 /* Determine the transport for this connection */ 542 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 543 it = &idm_transport_list[type]; 544 545 if (it->it_ops == NULL) { 546 /* transport is not registered */ 547 continue; 548 } 549 550 if (it->it_ops->it_conn_is_capable(cr, &caps)) { 551 return (it); 552 } 553 } 554 555 ASSERT(0); 556 return (NULL); /* Make gcc happy */ 557 } 558 559 void 560 idm_transport_setup(ldi_ident_t li, boolean_t boot_conn) 561 { 562 idm_transport_type_t type; 563 idm_transport_t *it; 564 int rc; 565 566 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 567 it = &idm_transport_list[type]; 568 /* 569 * We may want to store the LDI handle in the idm_svc_t 570 * and then allow multiple calls to ldi_open_by_name. This 571 * would enable the LDI code to track who has the device open 572 * which could be useful in the case where we have multiple 573 * services and perhaps also have initiator and target opening 574 * the transport simultaneously. For now we stick with the 575 * plan. 576 */ 577 if (it->it_ops == NULL) { 578 /* transport is not ready, try to initialize it */ 579 if (it->it_type == IDM_TRANSPORT_TYPE_SOCKETS) { 580 idm_so_init(it); 581 } else { 582 if (boot_conn == B_TRUE) { 583 /* 584 * iSCSI boot doesn't need iSER. 585 * Open iSER here may drive IO to 586 * a failed session and cause 587 * deadlock 588 */ 589 continue; 590 } 591 rc = ldi_open_by_name(it->it_device_path, 592 FREAD | FWRITE, kcred, &it->it_ldi_hdl, li); 593 /* 594 * If the open is successful we will have 595 * filled in the LDI handle in the transport 596 * table and we expect that the transport 597 * registered itself. 598 */ 599 if (rc != 0) { 600 it->it_ldi_hdl = NULL; 601 } 602 } 603 } 604 } 605 } 606 607 void 608 idm_transport_teardown() 609 { 610 idm_transport_type_t type; 611 idm_transport_t *it; 612 613 ASSERT(mutex_owned(&idm.idm_global_mutex)); 614 615 /* Caller holds the IDM global mutex */ 616 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 617 it = &idm_transport_list[type]; 618 /* If we have an open LDI handle on this driver, close it */ 619 if (it->it_ldi_hdl != NULL) { 620 (void) ldi_close(it->it_ldi_hdl, FNDELAY, kcred); 621 it->it_ldi_hdl = NULL; 622 } 623 } 624 } 625 626 /* 627 * ID pool code. We use this to generate unique structure identifiers without 628 * searching the existing structures. This avoids the need to lock entire 629 * sets of structures at inopportune times. Adapted from the CIFS server code. 630 * 631 * A pool of IDs is a pool of 16 bit numbers. It is implemented as a bitmap. 632 * A bit set to '1' indicates that that particular value has been allocated. 633 * The allocation process is done shifting a bit through the whole bitmap. 634 * The current position of that index bit is kept in the idm_idpool_t 635 * structure and represented by a byte index (0 to buffer size minus 1) and 636 * a bit index (0 to 7). 637 * 638 * The pools start with a size of 8 bytes or 64 IDs. Each time the pool runs 639 * out of IDs its current size is doubled until it reaches its maximum size 640 * (8192 bytes or 65536 IDs). The IDs 0 and 65535 are never given out which 641 * means that a pool can have a maximum number of 65534 IDs available. 642 */ 643 644 static int 645 idm_idpool_increment( 646 idm_idpool_t *pool) 647 { 648 uint8_t *new_pool; 649 uint32_t new_size; 650 651 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 652 653 new_size = pool->id_size * 2; 654 if (new_size <= IDM_IDPOOL_MAX_SIZE) { 655 new_pool = kmem_alloc(new_size / 8, KM_NOSLEEP); 656 if (new_pool) { 657 bzero(new_pool, new_size / 8); 658 bcopy(pool->id_pool, new_pool, pool->id_size / 8); 659 kmem_free(pool->id_pool, pool->id_size / 8); 660 pool->id_pool = new_pool; 661 pool->id_free_counter += new_size - pool->id_size; 662 pool->id_max_free_counter += new_size - pool->id_size; 663 pool->id_size = new_size; 664 pool->id_idx_msk = (new_size / 8) - 1; 665 if (new_size >= IDM_IDPOOL_MAX_SIZE) { 666 /* id -1 made unavailable */ 667 pool->id_pool[pool->id_idx_msk] = 0x80; 668 pool->id_free_counter--; 669 pool->id_max_free_counter--; 670 } 671 return (0); 672 } 673 } 674 return (-1); 675 } 676 677 /* 678 * idm_idpool_constructor 679 * 680 * This function initializes the pool structure provided. 681 */ 682 683 int 684 idm_idpool_create(idm_idpool_t *pool) 685 { 686 687 ASSERT(pool->id_magic != IDM_IDPOOL_MAGIC); 688 689 pool->id_size = IDM_IDPOOL_MIN_SIZE; 690 pool->id_idx_msk = (IDM_IDPOOL_MIN_SIZE / 8) - 1; 691 pool->id_free_counter = IDM_IDPOOL_MIN_SIZE - 1; 692 pool->id_max_free_counter = IDM_IDPOOL_MIN_SIZE - 1; 693 pool->id_bit = 0x02; 694 pool->id_bit_idx = 1; 695 pool->id_idx = 0; 696 pool->id_pool = (uint8_t *)kmem_alloc((IDM_IDPOOL_MIN_SIZE / 8), 697 KM_SLEEP); 698 bzero(pool->id_pool, (IDM_IDPOOL_MIN_SIZE / 8)); 699 /* -1 id made unavailable */ 700 pool->id_pool[0] = 0x01; /* id 0 made unavailable */ 701 mutex_init(&pool->id_mutex, NULL, MUTEX_DEFAULT, NULL); 702 pool->id_magic = IDM_IDPOOL_MAGIC; 703 return (0); 704 } 705 706 /* 707 * idm_idpool_destructor 708 * 709 * This function tears down and frees the resources associated with the 710 * pool provided. 711 */ 712 713 void 714 idm_idpool_destroy(idm_idpool_t *pool) 715 { 716 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 717 ASSERT(pool->id_free_counter == pool->id_max_free_counter); 718 pool->id_magic = (uint32_t)~IDM_IDPOOL_MAGIC; 719 mutex_destroy(&pool->id_mutex); 720 kmem_free(pool->id_pool, (size_t)(pool->id_size / 8)); 721 } 722 723 /* 724 * idm_idpool_alloc 725 * 726 * This function allocates an ID from the pool provided. 727 */ 728 int 729 idm_idpool_alloc(idm_idpool_t *pool, uint16_t *id) 730 { 731 uint32_t i; 732 uint8_t bit; 733 uint8_t bit_idx; 734 uint8_t byte; 735 736 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 737 738 mutex_enter(&pool->id_mutex); 739 if ((pool->id_free_counter == 0) && idm_idpool_increment(pool)) { 740 mutex_exit(&pool->id_mutex); 741 return (-1); 742 } 743 744 i = pool->id_size; 745 while (i) { 746 bit = pool->id_bit; 747 bit_idx = pool->id_bit_idx; 748 byte = pool->id_pool[pool->id_idx]; 749 while (bit) { 750 if (byte & bit) { 751 bit = bit << 1; 752 bit_idx++; 753 continue; 754 } 755 pool->id_pool[pool->id_idx] |= bit; 756 *id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx); 757 pool->id_free_counter--; 758 pool->id_bit = bit; 759 pool->id_bit_idx = bit_idx; 760 mutex_exit(&pool->id_mutex); 761 return (0); 762 } 763 pool->id_bit = 1; 764 pool->id_bit_idx = 0; 765 pool->id_idx++; 766 pool->id_idx &= pool->id_idx_msk; 767 --i; 768 } 769 /* 770 * This section of code shouldn't be reached. If there are IDs 771 * available and none could be found there's a problem. 772 */ 773 ASSERT(0); 774 mutex_exit(&pool->id_mutex); 775 return (-1); 776 } 777 778 /* 779 * idm_idpool_free 780 * 781 * This function frees the ID provided. 782 */ 783 void 784 idm_idpool_free(idm_idpool_t *pool, uint16_t id) 785 { 786 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 787 ASSERT(id != 0); 788 ASSERT(id != 0xFFFF); 789 790 mutex_enter(&pool->id_mutex); 791 if (pool->id_pool[id >> 3] & (1 << (id & 7))) { 792 pool->id_pool[id >> 3] &= ~(1 << (id & 7)); 793 pool->id_free_counter++; 794 ASSERT(pool->id_free_counter <= pool->id_max_free_counter); 795 mutex_exit(&pool->id_mutex); 796 return; 797 } 798 /* Freeing a free ID. */ 799 ASSERT(0); 800 mutex_exit(&pool->id_mutex); 801 } 802 803 uint32_t 804 idm_cid_alloc(void) 805 { 806 /* 807 * ID pool works with 16-bit identifiers right now. That should 808 * be plenty since we will probably never have more than 2^16 809 * connections simultaneously. 810 */ 811 uint16_t cid16; 812 813 if (idm_idpool_alloc(&idm.idm_conn_id_pool, &cid16) == -1) { 814 return (0); /* Fail */ 815 } 816 817 return ((uint32_t)cid16); 818 } 819 820 void 821 idm_cid_free(uint32_t cid) 822 { 823 idm_idpool_free(&idm.idm_conn_id_pool, (uint16_t)cid); 824 } 825 826 827 /* 828 * Code for generating the header and data digests 829 * 830 * This is the CRC-32C table 831 * Generated with: 832 * width = 32 bits 833 * poly = 0x1EDC6F41 834 * reflect input bytes = true 835 * reflect output bytes = true 836 */ 837 838 uint32_t idm_crc32c_table[256] = 839 { 840 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 841 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 842 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 843 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 844 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 845 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 846 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 847 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 848 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 849 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 850 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 851 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 852 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 853 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 854 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 855 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 856 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 857 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 858 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 859 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 860 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 861 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 862 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 863 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 864 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 865 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 866 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 867 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 868 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 869 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 870 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 871 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 872 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 873 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 874 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 875 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 876 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 877 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 878 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 879 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 880 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 881 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 882 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 883 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 884 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 885 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 886 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 887 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 888 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 889 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 890 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 891 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 892 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 893 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 894 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 895 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 896 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 897 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 898 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 899 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 900 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 901 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 902 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 903 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 904 }; 905 906 /* 907 * iscsi_crc32c - Steps through buffer one byte at at time, calculates 908 * reflected crc using table. 909 */ 910 uint32_t 911 idm_crc32c(void *address, unsigned long length) 912 { 913 uint8_t *buffer = address; 914 uint32_t crc = 0xffffffff, result; 915 #ifdef _BIG_ENDIAN 916 uint8_t byte0, byte1, byte2, byte3; 917 #endif 918 919 ASSERT(address != NULL); 920 921 if (iscsi_crc32_hd == -1) { 922 if (hd_crc32_avail((uint32_t *)idm_crc32c_table) == B_TRUE) { 923 iscsi_crc32_hd = 0; 924 } else { 925 iscsi_crc32_hd = 1; 926 } 927 } 928 if (iscsi_crc32_hd == 0) 929 return (HW_CRC32(buffer, length, crc)); 930 931 while (length--) { 932 crc = idm_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^ 933 (crc >> 8); 934 } 935 result = crc ^ 0xffffffff; 936 937 #ifdef _BIG_ENDIAN 938 byte0 = (uint8_t)(result & 0xFF); 939 byte1 = (uint8_t)((result >> 8) & 0xFF); 940 byte2 = (uint8_t)((result >> 16) & 0xFF); 941 byte3 = (uint8_t)((result >> 24) & 0xFF); 942 result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); 943 #endif /* _BIG_ENDIAN */ 944 945 return (result); 946 } 947 948 949 /* 950 * idm_crc32c_continued - Continues stepping through buffer one 951 * byte at at time, calculates reflected crc using table. 952 */ 953 uint32_t 954 idm_crc32c_continued(void *address, unsigned long length, uint32_t crc) 955 { 956 uint8_t *buffer = address; 957 uint32_t result; 958 #ifdef _BIG_ENDIAN 959 uint8_t byte0, byte1, byte2, byte3; 960 #endif 961 962 ASSERT(address != NULL); 963 964 if (iscsi_crc32_hd == -1) { 965 if (hd_crc32_avail((uint32_t *)idm_crc32c_table) == B_TRUE) { 966 iscsi_crc32_hd = 0; 967 } else { 968 iscsi_crc32_hd = 1; 969 } 970 } 971 if (iscsi_crc32_hd == 0) 972 return (HW_CRC32_CONT(buffer, length, crc)); 973 974 975 #ifdef _BIG_ENDIAN 976 byte0 = (uint8_t)((crc >> 24) & 0xFF); 977 byte1 = (uint8_t)((crc >> 16) & 0xFF); 978 byte2 = (uint8_t)((crc >> 8) & 0xFF); 979 byte3 = (uint8_t)(crc & 0xFF); 980 crc = ((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0); 981 #endif 982 983 crc = crc ^ 0xffffffff; 984 while (length--) { 985 crc = idm_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^ 986 (crc >> 8); 987 } 988 result = crc ^ 0xffffffff; 989 990 #ifdef _BIG_ENDIAN 991 byte0 = (uint8_t)(result & 0xFF); 992 byte1 = (uint8_t)((result >> 8) & 0xFF); 993 byte2 = (uint8_t)((result >> 16) & 0xFF); 994 byte3 = (uint8_t)((result >> 24) & 0xFF); 995 result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); 996 #endif 997 return (result); 998 } 999 1000 /* ARGSUSED */ 1001 int 1002 idm_task_constructor(void *hdl, void *arg, int flags) 1003 { 1004 idm_task_t *idt = (idm_task_t *)hdl; 1005 uint32_t next_task; 1006 1007 mutex_init(&idt->idt_mutex, NULL, MUTEX_DEFAULT, NULL); 1008 1009 /* Find the next free task ID */ 1010 rw_enter(&idm.idm_taskid_table_lock, RW_WRITER); 1011 next_task = idm.idm_taskid_next; 1012 while (idm.idm_taskid_table[next_task]) { 1013 next_task++; 1014 if (next_task == idm.idm_taskid_max) 1015 next_task = 0; 1016 if (next_task == idm.idm_taskid_next) { 1017 rw_exit(&idm.idm_taskid_table_lock); 1018 return (-1); 1019 } 1020 } 1021 1022 idm.idm_taskid_table[next_task] = idt; 1023 idm.idm_taskid_next = (next_task + 1) % idm.idm_taskid_max; 1024 rw_exit(&idm.idm_taskid_table_lock); 1025 1026 idt->idt_tt = next_task; 1027 1028 list_create(&idt->idt_inbufv, sizeof (idm_buf_t), 1029 offsetof(idm_buf_t, idb_buflink)); 1030 list_create(&idt->idt_outbufv, sizeof (idm_buf_t), 1031 offsetof(idm_buf_t, idb_buflink)); 1032 idm_refcnt_init(&idt->idt_refcnt, idt); 1033 1034 /* 1035 * Set the transport header pointer explicitly. This removes the 1036 * need for per-transport header allocation, which simplifies cache 1037 * init considerably. If at a later date we have an additional IDM 1038 * transport that requires a different size, we'll revisit this. 1039 */ 1040 idt->idt_transport_hdr = (void *)(idt + 1); /* pointer arithmetic */ 1041 idt->idt_flags = 0; 1042 return (0); 1043 } 1044 1045 /* ARGSUSED */ 1046 void 1047 idm_task_destructor(void *hdl, void *arg) 1048 { 1049 idm_task_t *idt = (idm_task_t *)hdl; 1050 1051 /* Remove the task from the ID table */ 1052 rw_enter(&idm.idm_taskid_table_lock, RW_WRITER); 1053 idm.idm_taskid_table[idt->idt_tt] = NULL; 1054 rw_exit(&idm.idm_taskid_table_lock); 1055 1056 /* free the inbuf and outbuf */ 1057 idm_refcnt_destroy(&idt->idt_refcnt); 1058 list_destroy(&idt->idt_inbufv); 1059 list_destroy(&idt->idt_outbufv); 1060 1061 /* 1062 * The final call to idm_task_rele may happen with the task 1063 * mutex held which may invoke this destructor immediately. 1064 * Stall here until the task mutex owner lets go. 1065 */ 1066 mutex_enter(&idt->idt_mutex); 1067 mutex_destroy(&idt->idt_mutex); 1068 } 1069 1070 /* 1071 * idm_listbuf_insert searches from the back of the list looking for the 1072 * insertion point. 1073 */ 1074 void 1075 idm_listbuf_insert(list_t *lst, idm_buf_t *buf) 1076 { 1077 idm_buf_t *idb; 1078 1079 /* iterate through the list to find the insertion point */ 1080 for (idb = list_tail(lst); idb != NULL; idb = list_prev(lst, idb)) { 1081 1082 if (idb->idb_bufoffset < buf->idb_bufoffset) { 1083 1084 list_insert_after(lst, idb, buf); 1085 return; 1086 } 1087 } 1088 1089 /* add the buf to the head of the list */ 1090 list_insert_head(lst, buf); 1091 1092 } 1093 1094 /*ARGSUSED*/ 1095 void 1096 idm_wd_thread(void *arg) 1097 { 1098 idm_conn_t *ic; 1099 clock_t wake_time = SEC_TO_TICK(IDM_WD_INTERVAL); 1100 clock_t idle_time; 1101 1102 /* Record the thread id for thread_join() */ 1103 idm.idm_wd_thread_did = curthread->t_did; 1104 mutex_enter(&idm.idm_global_mutex); 1105 idm.idm_wd_thread_running = B_TRUE; 1106 cv_signal(&idm.idm_wd_cv); 1107 1108 while (idm.idm_wd_thread_running) { 1109 for (ic = list_head(&idm.idm_tgt_conn_list); 1110 ic != NULL; 1111 ic = list_next(&idm.idm_tgt_conn_list, ic)) { 1112 idle_time = ddi_get_lbolt() - ic->ic_timestamp; 1113 1114 /* 1115 * If this connection is in FFP then grab a hold 1116 * and check the various timeout thresholds. Otherwise 1117 * the connection is closing and we should just 1118 * move on to the next one. 1119 */ 1120 mutex_enter(&ic->ic_state_mutex); 1121 if (ic->ic_ffp) { 1122 idm_conn_hold(ic); 1123 } else { 1124 mutex_exit(&ic->ic_state_mutex); 1125 continue; 1126 } 1127 1128 /* 1129 * If there hasn't been any activity on this 1130 * connection for the keepalive timeout period 1131 * and if the client has provided a keepalive 1132 * callback then call the keepalive callback. 1133 * This allows the client to take action to keep 1134 * the link alive (like send a nop PDU). 1135 */ 1136 if ((TICK_TO_SEC(idle_time) >= 1137 IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT) && 1138 !ic->ic_keepalive) { 1139 ic->ic_keepalive = B_TRUE; 1140 if (ic->ic_conn_ops.icb_keepalive) { 1141 mutex_exit(&ic->ic_state_mutex); 1142 mutex_exit(&idm.idm_global_mutex); 1143 (*ic->ic_conn_ops.icb_keepalive)(ic); 1144 mutex_enter(&idm.idm_global_mutex); 1145 mutex_enter(&ic->ic_state_mutex); 1146 } 1147 } else if ((TICK_TO_SEC(idle_time) < 1148 IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT)) { 1149 /* Reset keepalive */ 1150 ic->ic_keepalive = B_FALSE; 1151 } 1152 1153 /* 1154 * If there hasn't been any activity on this 1155 * connection for the failure timeout period then 1156 * drop the connection. We expect the initiator 1157 * to keep the connection alive if it wants the 1158 * connection to stay open. 1159 * 1160 * If it turns out to be desireable to take a 1161 * more active role in maintaining the connect 1162 * we could add a client callback to send 1163 * a "keepalive" kind of message (no doubt a nop) 1164 * and fire that on a shorter timer. 1165 */ 1166 if (TICK_TO_SEC(idle_time) > 1167 IDM_TRANSPORT_FAIL_IDLE_TIMEOUT) { 1168 mutex_exit(&ic->ic_state_mutex); 1169 mutex_exit(&idm.idm_global_mutex); 1170 IDM_SM_LOG(CE_WARN, "idm_wd_thread: " 1171 "conn %p idle for %d seconds, " 1172 "sending CE_TRANSPORT_FAIL", 1173 (void *)ic, (int)idle_time); 1174 idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL); 1175 mutex_enter(&idm.idm_global_mutex); 1176 mutex_enter(&ic->ic_state_mutex); 1177 } 1178 1179 idm_conn_rele(ic); 1180 1181 mutex_exit(&ic->ic_state_mutex); 1182 } 1183 1184 (void) cv_reltimedwait(&idm.idm_wd_cv, &idm.idm_global_mutex, 1185 wake_time, TR_CLOCK_TICK); 1186 } 1187 mutex_exit(&idm.idm_global_mutex); 1188 1189 thread_exit(); 1190 } 1191