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 2009 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 ic->ic_svc_binding = is; 406 407 /* 408 * Prepare connection state machine 409 */ 410 if ((rc = idm_conn_sm_init(ic)) != 0) { 411 idm_conn_destroy_common(ic); 412 return (rc); 413 } 414 415 416 *ic_result = ic; 417 418 mutex_enter(&idm.idm_global_mutex); 419 list_insert_tail(&idm.idm_tgt_conn_list, ic); 420 idm.idm_tgt_conn_count++; 421 mutex_exit(&idm.idm_global_mutex); 422 423 return (IDM_STATUS_SUCCESS); 424 } 425 426 void 427 idm_svc_conn_destroy(idm_conn_t *ic) 428 { 429 mutex_enter(&idm.idm_global_mutex); 430 list_remove(&idm.idm_tgt_conn_list, ic); 431 idm.idm_tgt_conn_count--; 432 mutex_exit(&idm.idm_global_mutex); 433 434 if (ic->ic_transport_private != NULL) { 435 ic->ic_transport_ops->it_tgt_conn_destroy(ic); 436 } 437 idm_conn_destroy_common(ic); 438 } 439 440 /* 441 * idm_conn_create_common() 442 * 443 * Allocate and initialize IDM connection context 444 */ 445 idm_conn_t * 446 idm_conn_create_common(idm_conn_type_t conn_type, idm_transport_type_t tt, 447 idm_conn_ops_t *conn_ops) 448 { 449 idm_conn_t *ic; 450 idm_transport_t *it; 451 idm_transport_type_t type; 452 453 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 454 it = &idm_transport_list[type]; 455 456 if ((it->it_ops != NULL) && (it->it_type == tt)) 457 break; 458 } 459 ASSERT(it->it_type == tt); 460 if (it->it_type != tt) 461 return (NULL); 462 463 ic = kmem_zalloc(sizeof (idm_conn_t), KM_SLEEP); 464 465 /* Initialize data */ 466 ic->ic_target_name[0] = '\0'; 467 ic->ic_initiator_name[0] = '\0'; 468 ic->ic_isid[0] = '\0'; 469 ic->ic_tsih[0] = '\0'; 470 ic->ic_conn_type = conn_type; 471 ic->ic_conn_ops = *conn_ops; 472 ic->ic_transport_ops = it->it_ops; 473 ic->ic_transport_type = tt; 474 ic->ic_transport_private = NULL; /* Set by transport service */ 475 ic->ic_internal_cid = idm_cid_alloc(); 476 if (ic->ic_internal_cid == 0) { 477 kmem_free(ic, sizeof (idm_conn_t)); 478 return (NULL); 479 } 480 mutex_init(&ic->ic_mutex, NULL, MUTEX_DEFAULT, NULL); 481 cv_init(&ic->ic_cv, NULL, CV_DEFAULT, NULL); 482 idm_refcnt_init(&ic->ic_refcnt, ic); 483 484 return (ic); 485 } 486 487 void 488 idm_conn_destroy_common(idm_conn_t *ic) 489 { 490 idm_conn_sm_fini(ic); 491 idm_refcnt_destroy(&ic->ic_refcnt); 492 cv_destroy(&ic->ic_cv); 493 mutex_destroy(&ic->ic_mutex); 494 idm_cid_free(ic->ic_internal_cid); 495 496 kmem_free(ic, sizeof (idm_conn_t)); 497 } 498 499 /* 500 * Invoked from the SM as a result of client's invocation of 501 * idm_ini_conn_connect() 502 */ 503 idm_status_t 504 idm_ini_conn_finish(idm_conn_t *ic) 505 { 506 /* invoke transport-specific connection */ 507 return (ic->ic_transport_ops->it_ini_conn_connect(ic)); 508 } 509 510 idm_status_t 511 idm_tgt_conn_finish(idm_conn_t *ic) 512 { 513 idm_status_t rc; 514 515 rc = idm_notify_client(ic, CN_CONNECT_ACCEPT, NULL); 516 if (rc != IDM_STATUS_SUCCESS) { 517 return (IDM_STATUS_REJECT); 518 } 519 520 /* Target client is ready to receive a login, start connection */ 521 return (ic->ic_transport_ops->it_tgt_conn_connect(ic)); 522 } 523 524 idm_transport_t * 525 idm_transport_lookup(idm_conn_req_t *cr) 526 { 527 idm_transport_type_t type; 528 idm_transport_t *it; 529 idm_transport_caps_t caps; 530 531 /* 532 * Make sure all available transports are setup. We call this now 533 * instead of at initialization time in case IB has become available 534 * since we started (hotplug, etc). 535 */ 536 idm_transport_setup(cr->cr_li); 537 538 /* Determine the transport for this connection */ 539 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 540 it = &idm_transport_list[type]; 541 542 if (it->it_ops == NULL) { 543 /* transport is not registered */ 544 continue; 545 } 546 547 if (it->it_ops->it_conn_is_capable(cr, &caps)) { 548 return (it); 549 } 550 } 551 552 ASSERT(0); 553 return (NULL); /* Make gcc happy */ 554 } 555 556 void 557 idm_transport_setup(ldi_ident_t li) 558 { 559 idm_transport_type_t type; 560 idm_transport_t *it; 561 int rc; 562 563 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 564 it = &idm_transport_list[type]; 565 /* 566 * We may want to store the LDI handle in the idm_svc_t 567 * and then allow multiple calls to ldi_open_by_name. This 568 * would enable the LDI code to track who has the device open 569 * which could be useful in the case where we have multiple 570 * services and perhaps also have initiator and target opening 571 * the transport simultaneously. For now we stick with the 572 * plan. 573 */ 574 if (it->it_ops == NULL) { 575 /* transport is not ready, try to initialize it */ 576 if (it->it_type == IDM_TRANSPORT_TYPE_SOCKETS) { 577 idm_so_init(it); 578 } else { 579 rc = ldi_open_by_name(it->it_device_path, 580 FREAD | FWRITE, kcred, &it->it_ldi_hdl, li); 581 /* 582 * If the open is successful we will have 583 * filled in the LDI handle in the transport 584 * table and we expect that the transport 585 * registered itself. 586 */ 587 if (rc != 0) { 588 it->it_ldi_hdl = NULL; 589 } 590 } 591 } 592 } 593 } 594 595 void 596 idm_transport_teardown() 597 { 598 idm_transport_type_t type; 599 idm_transport_t *it; 600 601 ASSERT(mutex_owned(&idm.idm_global_mutex)); 602 603 /* Caller holds the IDM global mutex */ 604 for (type = 0; type < IDM_TRANSPORT_NUM_TYPES; type++) { 605 it = &idm_transport_list[type]; 606 /* If we have an open LDI handle on this driver, close it */ 607 if (it->it_ldi_hdl != NULL) { 608 (void) ldi_close(it->it_ldi_hdl, FNDELAY, kcred); 609 it->it_ldi_hdl = NULL; 610 } 611 } 612 } 613 614 /* 615 * ID pool code. We use this to generate unique structure identifiers without 616 * searching the existing structures. This avoids the need to lock entire 617 * sets of structures at inopportune times. Adapted from the CIFS server code. 618 * 619 * A pool of IDs is a pool of 16 bit numbers. It is implemented as a bitmap. 620 * A bit set to '1' indicates that that particular value has been allocated. 621 * The allocation process is done shifting a bit through the whole bitmap. 622 * The current position of that index bit is kept in the idm_idpool_t 623 * structure and represented by a byte index (0 to buffer size minus 1) and 624 * a bit index (0 to 7). 625 * 626 * The pools start with a size of 8 bytes or 64 IDs. Each time the pool runs 627 * out of IDs its current size is doubled until it reaches its maximum size 628 * (8192 bytes or 65536 IDs). The IDs 0 and 65535 are never given out which 629 * means that a pool can have a maximum number of 65534 IDs available. 630 */ 631 632 static int 633 idm_idpool_increment( 634 idm_idpool_t *pool) 635 { 636 uint8_t *new_pool; 637 uint32_t new_size; 638 639 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 640 641 new_size = pool->id_size * 2; 642 if (new_size <= IDM_IDPOOL_MAX_SIZE) { 643 new_pool = kmem_alloc(new_size / 8, KM_NOSLEEP); 644 if (new_pool) { 645 bzero(new_pool, new_size / 8); 646 bcopy(pool->id_pool, new_pool, pool->id_size / 8); 647 kmem_free(pool->id_pool, pool->id_size / 8); 648 pool->id_pool = new_pool; 649 pool->id_free_counter += new_size - pool->id_size; 650 pool->id_max_free_counter += new_size - pool->id_size; 651 pool->id_size = new_size; 652 pool->id_idx_msk = (new_size / 8) - 1; 653 if (new_size >= IDM_IDPOOL_MAX_SIZE) { 654 /* id -1 made unavailable */ 655 pool->id_pool[pool->id_idx_msk] = 0x80; 656 pool->id_free_counter--; 657 pool->id_max_free_counter--; 658 } 659 return (0); 660 } 661 } 662 return (-1); 663 } 664 665 /* 666 * idm_idpool_constructor 667 * 668 * This function initializes the pool structure provided. 669 */ 670 671 int 672 idm_idpool_create(idm_idpool_t *pool) 673 { 674 675 ASSERT(pool->id_magic != IDM_IDPOOL_MAGIC); 676 677 pool->id_size = IDM_IDPOOL_MIN_SIZE; 678 pool->id_idx_msk = (IDM_IDPOOL_MIN_SIZE / 8) - 1; 679 pool->id_free_counter = IDM_IDPOOL_MIN_SIZE - 1; 680 pool->id_max_free_counter = IDM_IDPOOL_MIN_SIZE - 1; 681 pool->id_bit = 0x02; 682 pool->id_bit_idx = 1; 683 pool->id_idx = 0; 684 pool->id_pool = (uint8_t *)kmem_alloc((IDM_IDPOOL_MIN_SIZE / 8), 685 KM_SLEEP); 686 bzero(pool->id_pool, (IDM_IDPOOL_MIN_SIZE / 8)); 687 /* -1 id made unavailable */ 688 pool->id_pool[0] = 0x01; /* id 0 made unavailable */ 689 mutex_init(&pool->id_mutex, NULL, MUTEX_DEFAULT, NULL); 690 pool->id_magic = IDM_IDPOOL_MAGIC; 691 return (0); 692 } 693 694 /* 695 * idm_idpool_destructor 696 * 697 * This function tears down and frees the resources associated with the 698 * pool provided. 699 */ 700 701 void 702 idm_idpool_destroy(idm_idpool_t *pool) 703 { 704 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 705 ASSERT(pool->id_free_counter == pool->id_max_free_counter); 706 pool->id_magic = (uint32_t)~IDM_IDPOOL_MAGIC; 707 mutex_destroy(&pool->id_mutex); 708 kmem_free(pool->id_pool, (size_t)(pool->id_size / 8)); 709 } 710 711 /* 712 * idm_idpool_alloc 713 * 714 * This function allocates an ID from the pool provided. 715 */ 716 int 717 idm_idpool_alloc(idm_idpool_t *pool, uint16_t *id) 718 { 719 uint32_t i; 720 uint8_t bit; 721 uint8_t bit_idx; 722 uint8_t byte; 723 724 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 725 726 mutex_enter(&pool->id_mutex); 727 if ((pool->id_free_counter == 0) && idm_idpool_increment(pool)) { 728 mutex_exit(&pool->id_mutex); 729 return (-1); 730 } 731 732 i = pool->id_size; 733 while (i) { 734 bit = pool->id_bit; 735 bit_idx = pool->id_bit_idx; 736 byte = pool->id_pool[pool->id_idx]; 737 while (bit) { 738 if (byte & bit) { 739 bit = bit << 1; 740 bit_idx++; 741 continue; 742 } 743 pool->id_pool[pool->id_idx] |= bit; 744 *id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx); 745 pool->id_free_counter--; 746 pool->id_bit = bit; 747 pool->id_bit_idx = bit_idx; 748 mutex_exit(&pool->id_mutex); 749 return (0); 750 } 751 pool->id_bit = 1; 752 pool->id_bit_idx = 0; 753 pool->id_idx++; 754 pool->id_idx &= pool->id_idx_msk; 755 --i; 756 } 757 /* 758 * This section of code shouldn't be reached. If there are IDs 759 * available and none could be found there's a problem. 760 */ 761 ASSERT(0); 762 mutex_exit(&pool->id_mutex); 763 return (-1); 764 } 765 766 /* 767 * idm_idpool_free 768 * 769 * This function frees the ID provided. 770 */ 771 void 772 idm_idpool_free(idm_idpool_t *pool, uint16_t id) 773 { 774 ASSERT(pool->id_magic == IDM_IDPOOL_MAGIC); 775 ASSERT(id != 0); 776 ASSERT(id != 0xFFFF); 777 778 mutex_enter(&pool->id_mutex); 779 if (pool->id_pool[id >> 3] & (1 << (id & 7))) { 780 pool->id_pool[id >> 3] &= ~(1 << (id & 7)); 781 pool->id_free_counter++; 782 ASSERT(pool->id_free_counter <= pool->id_max_free_counter); 783 mutex_exit(&pool->id_mutex); 784 return; 785 } 786 /* Freeing a free ID. */ 787 ASSERT(0); 788 mutex_exit(&pool->id_mutex); 789 } 790 791 uint32_t 792 idm_cid_alloc(void) 793 { 794 /* 795 * ID pool works with 16-bit identifiers right now. That should 796 * be plenty since we will probably never have more than 2^16 797 * connections simultaneously. 798 */ 799 uint16_t cid16; 800 801 if (idm_idpool_alloc(&idm.idm_conn_id_pool, &cid16) == -1) { 802 return (0); /* Fail */ 803 } 804 805 return ((uint32_t)cid16); 806 } 807 808 void 809 idm_cid_free(uint32_t cid) 810 { 811 idm_idpool_free(&idm.idm_conn_id_pool, (uint16_t)cid); 812 } 813 814 815 /* 816 * Code for generating the header and data digests 817 * 818 * This is the CRC-32C table 819 * Generated with: 820 * width = 32 bits 821 * poly = 0x1EDC6F41 822 * reflect input bytes = true 823 * reflect output bytes = true 824 */ 825 826 uint32_t idm_crc32c_table[256] = 827 { 828 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 829 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 830 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 831 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 832 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 833 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 834 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 835 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 836 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 837 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 838 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 839 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 840 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 841 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 842 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 843 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 844 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 845 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 846 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 847 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 848 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 849 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 850 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 851 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 852 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 853 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 854 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 855 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 856 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 857 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 858 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 859 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 860 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 861 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 862 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 863 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 864 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 865 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 866 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 867 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 868 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 869 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 870 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 871 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 872 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 873 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 874 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 875 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 876 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 877 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 878 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 879 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 880 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 881 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 882 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 883 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 884 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 885 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 886 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 887 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 888 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 889 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 890 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 891 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 892 }; 893 894 /* 895 * iscsi_crc32c - Steps through buffer one byte at at time, calculates 896 * reflected crc using table. 897 */ 898 uint32_t 899 idm_crc32c(void *address, unsigned long length) 900 { 901 uint8_t *buffer = address; 902 uint32_t crc = 0xffffffff, result; 903 #ifdef _BIG_ENDIAN 904 uint8_t byte0, byte1, byte2, byte3; 905 #endif 906 907 ASSERT(address != NULL); 908 909 if (iscsi_crc32_hd == -1) { 910 if (hd_crc32_avail((uint32_t *)idm_crc32c_table) == B_TRUE) { 911 iscsi_crc32_hd = 0; 912 } else { 913 iscsi_crc32_hd = 1; 914 } 915 } 916 if (iscsi_crc32_hd == 0) 917 return (HW_CRC32(buffer, length, crc)); 918 919 while (length--) { 920 crc = idm_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^ 921 (crc >> 8); 922 } 923 result = crc ^ 0xffffffff; 924 925 #ifdef _BIG_ENDIAN 926 byte0 = (uint8_t)(result & 0xFF); 927 byte1 = (uint8_t)((result >> 8) & 0xFF); 928 byte2 = (uint8_t)((result >> 16) & 0xFF); 929 byte3 = (uint8_t)((result >> 24) & 0xFF); 930 result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); 931 #endif /* _BIG_ENDIAN */ 932 933 return (result); 934 } 935 936 937 /* 938 * idm_crc32c_continued - Continues stepping through buffer one 939 * byte at at time, calculates reflected crc using table. 940 */ 941 uint32_t 942 idm_crc32c_continued(void *address, unsigned long length, uint32_t crc) 943 { 944 uint8_t *buffer = address; 945 uint32_t result; 946 #ifdef _BIG_ENDIAN 947 uint8_t byte0, byte1, byte2, byte3; 948 #endif 949 950 ASSERT(address != NULL); 951 952 if (iscsi_crc32_hd == -1) { 953 if (hd_crc32_avail((uint32_t *)idm_crc32c_table) == B_TRUE) { 954 iscsi_crc32_hd = 0; 955 } else { 956 iscsi_crc32_hd = 1; 957 } 958 } 959 if (iscsi_crc32_hd == 0) 960 return (HW_CRC32_CONT(buffer, length, crc)); 961 962 963 #ifdef _BIG_ENDIAN 964 byte0 = (uint8_t)((crc >> 24) & 0xFF); 965 byte1 = (uint8_t)((crc >> 16) & 0xFF); 966 byte2 = (uint8_t)((crc >> 8) & 0xFF); 967 byte3 = (uint8_t)(crc & 0xFF); 968 crc = ((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0); 969 #endif 970 971 crc = crc ^ 0xffffffff; 972 while (length--) { 973 crc = idm_crc32c_table[(crc ^ *buffer++) & 0xFFL] ^ 974 (crc >> 8); 975 } 976 result = crc ^ 0xffffffff; 977 978 #ifdef _BIG_ENDIAN 979 byte0 = (uint8_t)(result & 0xFF); 980 byte1 = (uint8_t)((result >> 8) & 0xFF); 981 byte2 = (uint8_t)((result >> 16) & 0xFF); 982 byte3 = (uint8_t)((result >> 24) & 0xFF); 983 result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); 984 #endif 985 return (result); 986 } 987 988 /* ARGSUSED */ 989 int 990 idm_task_constructor(void *hdl, void *arg, int flags) 991 { 992 idm_task_t *idt = (idm_task_t *)hdl; 993 uint32_t next_task; 994 995 mutex_init(&idt->idt_mutex, NULL, MUTEX_DEFAULT, NULL); 996 997 /* Find the next free task ID */ 998 rw_enter(&idm.idm_taskid_table_lock, RW_WRITER); 999 next_task = idm.idm_taskid_next; 1000 while (idm.idm_taskid_table[next_task]) { 1001 next_task++; 1002 if (next_task == idm.idm_taskid_max) 1003 next_task = 0; 1004 if (next_task == idm.idm_taskid_next) { 1005 rw_exit(&idm.idm_taskid_table_lock); 1006 return (-1); 1007 } 1008 } 1009 1010 idm.idm_taskid_table[next_task] = idt; 1011 idm.idm_taskid_next = (next_task + 1) % idm.idm_taskid_max; 1012 rw_exit(&idm.idm_taskid_table_lock); 1013 1014 idt->idt_tt = next_task; 1015 1016 list_create(&idt->idt_inbufv, sizeof (idm_buf_t), 1017 offsetof(idm_buf_t, idb_buflink)); 1018 list_create(&idt->idt_outbufv, sizeof (idm_buf_t), 1019 offsetof(idm_buf_t, idb_buflink)); 1020 idm_refcnt_init(&idt->idt_refcnt, idt); 1021 1022 /* 1023 * Set the transport header pointer explicitly. This removes the 1024 * need for per-transport header allocation, which simplifies cache 1025 * init considerably. If at a later date we have an additional IDM 1026 * transport that requires a different size, we'll revisit this. 1027 */ 1028 idt->idt_transport_hdr = (void *)(idt + 1); /* pointer arithmetic */ 1029 idt->idt_flags = 0; 1030 return (0); 1031 } 1032 1033 /* ARGSUSED */ 1034 void 1035 idm_task_destructor(void *hdl, void *arg) 1036 { 1037 idm_task_t *idt = (idm_task_t *)hdl; 1038 1039 /* Remove the task from the ID table */ 1040 rw_enter(&idm.idm_taskid_table_lock, RW_WRITER); 1041 idm.idm_taskid_table[idt->idt_tt] = NULL; 1042 rw_exit(&idm.idm_taskid_table_lock); 1043 1044 /* free the inbuf and outbuf */ 1045 idm_refcnt_destroy(&idt->idt_refcnt); 1046 list_destroy(&idt->idt_inbufv); 1047 list_destroy(&idt->idt_outbufv); 1048 1049 /* 1050 * The final call to idm_task_rele may happen with the task 1051 * mutex held which may invoke this destructor immediately. 1052 * Stall here until the task mutex owner lets go. 1053 */ 1054 mutex_enter(&idt->idt_mutex); 1055 mutex_destroy(&idt->idt_mutex); 1056 } 1057 1058 /* 1059 * idm_listbuf_insert searches from the back of the list looking for the 1060 * insertion point. 1061 */ 1062 void 1063 idm_listbuf_insert(list_t *lst, idm_buf_t *buf) 1064 { 1065 idm_buf_t *idb; 1066 1067 /* iterate through the list to find the insertion point */ 1068 for (idb = list_tail(lst); idb != NULL; idb = list_prev(lst, idb)) { 1069 1070 if (idb->idb_bufoffset < buf->idb_bufoffset) { 1071 1072 list_insert_after(lst, idb, buf); 1073 return; 1074 } 1075 } 1076 1077 /* add the buf to the head of the list */ 1078 list_insert_head(lst, buf); 1079 1080 } 1081 1082 /*ARGSUSED*/ 1083 void 1084 idm_wd_thread(void *arg) 1085 { 1086 idm_conn_t *ic; 1087 clock_t wake_time = SEC_TO_TICK(IDM_WD_INTERVAL); 1088 clock_t idle_time; 1089 1090 /* Record the thread id for thread_join() */ 1091 idm.idm_wd_thread_did = curthread->t_did; 1092 mutex_enter(&idm.idm_global_mutex); 1093 idm.idm_wd_thread_running = B_TRUE; 1094 cv_signal(&idm.idm_wd_cv); 1095 1096 while (idm.idm_wd_thread_running) { 1097 for (ic = list_head(&idm.idm_tgt_conn_list); 1098 ic != NULL; 1099 ic = list_next(&idm.idm_tgt_conn_list, ic)) { 1100 idle_time = ddi_get_lbolt() - ic->ic_timestamp; 1101 1102 /* 1103 * If this connection is in FFP then grab a hold 1104 * and check the various timeout thresholds. Otherwise 1105 * the connection is closing and we should just 1106 * move on to the next one. 1107 */ 1108 mutex_enter(&ic->ic_state_mutex); 1109 if (ic->ic_ffp) { 1110 idm_conn_hold(ic); 1111 } else { 1112 mutex_exit(&ic->ic_state_mutex); 1113 continue; 1114 } 1115 1116 /* 1117 * If there hasn't been any activity on this 1118 * connection for the keepalive timeout period 1119 * and if the client has provided a keepalive 1120 * callback then call the keepalive callback. 1121 * This allows the client to take action to keep 1122 * the link alive (like send a nop PDU). 1123 */ 1124 if ((TICK_TO_SEC(idle_time) >= 1125 IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT) && 1126 !ic->ic_keepalive) { 1127 ic->ic_keepalive = B_TRUE; 1128 if (ic->ic_conn_ops.icb_keepalive) { 1129 mutex_exit(&ic->ic_state_mutex); 1130 mutex_exit(&idm.idm_global_mutex); 1131 (*ic->ic_conn_ops.icb_keepalive)(ic); 1132 mutex_enter(&idm.idm_global_mutex); 1133 mutex_enter(&ic->ic_state_mutex); 1134 } 1135 } else if ((TICK_TO_SEC(idle_time) < 1136 IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT)) { 1137 /* Reset keepalive */ 1138 ic->ic_keepalive = B_FALSE; 1139 } 1140 1141 /* 1142 * If there hasn't been any activity on this 1143 * connection for the failure timeout period then 1144 * drop the connection. We expect the initiator 1145 * to keep the connection alive if it wants the 1146 * connection to stay open. 1147 * 1148 * If it turns out to be desireable to take a 1149 * more active role in maintaining the connect 1150 * we could add a client callback to send 1151 * a "keepalive" kind of message (no doubt a nop) 1152 * and fire that on a shorter timer. 1153 */ 1154 if (TICK_TO_SEC(idle_time) > 1155 IDM_TRANSPORT_FAIL_IDLE_TIMEOUT) { 1156 mutex_exit(&ic->ic_state_mutex); 1157 mutex_exit(&idm.idm_global_mutex); 1158 IDM_SM_LOG(CE_WARN, "idm_wd_thread: " 1159 "conn %p idle for %d seconds, " 1160 "sending CE_TRANSPORT_FAIL", 1161 (void *)ic, (int)idle_time); 1162 idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL); 1163 mutex_enter(&idm.idm_global_mutex); 1164 mutex_enter(&ic->ic_state_mutex); 1165 } 1166 1167 idm_conn_rele(ic); 1168 1169 mutex_exit(&ic->ic_state_mutex); 1170 } 1171 1172 (void) cv_reltimedwait(&idm.idm_wd_cv, &idm.idm_global_mutex, 1173 wake_time, TR_CLOCK_TICK); 1174 } 1175 mutex_exit(&idm.idm_global_mutex); 1176 1177 thread_exit(); 1178 } 1179