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