1 2314 mcneal /* 2 2314 mcneal * CDDL HEADER START 3 2314 mcneal * 4 2314 mcneal * The contents of this file are subject to the terms of the 5 2314 mcneal * Common Development and Distribution License (the "License"). 6 2314 mcneal * You may not use this file except in compliance with the License. 7 2314 mcneal * 8 2314 mcneal * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 2314 mcneal * or http://www.opensolaris.org/os/licensing. 10 2314 mcneal * See the License for the specific language governing permissions 11 2314 mcneal * and limitations under the License. 12 2314 mcneal * 13 2314 mcneal * When distributing Covered Code, include this CDDL HEADER in each 14 2314 mcneal * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 2314 mcneal * If applicable, add the following below this CDDL HEADER, with the 16 2314 mcneal * fields enclosed by brackets "[]" replaced with your own identifying 17 2314 mcneal * information: Portions Copyright [yyyy] [name of copyright owner] 18 2314 mcneal * 19 2314 mcneal * CDDL HEADER END 20 2314 mcneal */ 21 2314 mcneal 22 2314 mcneal /* 23 6086 ts143224 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 2314 mcneal * Use is subject to license terms. 25 2314 mcneal */ 26 2314 mcneal 27 2314 mcneal /* 28 2314 mcneal * This file contains methods to handle the iSCSI Full Feature Phase aspects 29 2314 mcneal * of the protocol. 30 2314 mcneal */ 31 2314 mcneal 32 2314 mcneal #include <unistd.h> 33 2314 mcneal #include <poll.h> 34 2314 mcneal #include <strings.h> 35 2314 mcneal #include <stdlib.h> 36 2314 mcneal #include <sys/types.h> 37 2314 mcneal #include <assert.h> 38 2314 mcneal #include <stdio.h> 39 2314 mcneal #include <errno.h> 40 2314 mcneal #include <utility.h> 41 2314 mcneal #include <netinet/in.h> 42 2314 mcneal #include <inttypes.h> 43 3022 mcneal #include <sys/socket.h> 44 2314 mcneal #include <sys/iscsi_protocol.h> 45 2314 mcneal 46 4568 ahl #include <arpa/inet.h> 47 4568 ahl 48 2314 mcneal #include "iscsi_ffp.h" 49 2314 mcneal #include "iscsi_cmd.h" 50 2314 mcneal #include "t10_spc.h" 51 2314 mcneal #include "utility.h" 52 4568 ahl #include "iscsi_provider_impl.h" 53 2314 mcneal 54 2314 mcneal static Boolean_t handle_text_msg(iscsi_conn_t *, iscsi_hdr_t *, char *, int); 55 2314 mcneal static Boolean_t handle_logout_msg(iscsi_conn_t *, iscsi_hdr_t *, char *, int); 56 2314 mcneal static Boolean_t handle_scsi_cmd(iscsi_conn_t *, iscsi_hdr_t *, char *, int); 57 2314 mcneal static Boolean_t handle_noop_cmd(iscsi_conn_t *, iscsi_hdr_t *, char *, int); 58 2314 mcneal static Boolean_t handle_scsi_data(iscsi_conn_t *, iscsi_hdr_t *, char *, int); 59 2314 mcneal static Boolean_t handle_task_mgt(iscsi_conn_t *, iscsi_hdr_t *, char *, int); 60 2314 mcneal static Boolean_t dataout_delayed(iscsi_cmd_t *cmd, msg_type_t type); 61 2314 mcneal void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer); 62 2314 mcneal 63 2314 mcneal Boolean_t 64 2314 mcneal iscsi_full_feature(iscsi_conn_t *c) 65 2314 mcneal { 66 2314 mcneal iscsi_hdr_t h; 67 2314 mcneal Boolean_t rval = False; 68 4568 ahl char debug[128]; 69 4568 ahl char *ahs = NULL; 70 4568 ahl int cc; 71 4568 ahl int ahslen; 72 2314 mcneal 73 3022 mcneal if ((cc = recv(c->c_fd, &h, sizeof (h), MSG_WAITALL)) != sizeof (h)) { 74 2314 mcneal if (errno == ECONNRESET) { 75 2314 mcneal (void) snprintf(debug, sizeof (debug), 76 6913 ts143224 "CON%x full_feature -- initiator reset socket\n", 77 2314 mcneal c->c_num); 78 2314 mcneal } else { 79 2314 mcneal (void) snprintf(debug, sizeof (debug), 80 6913 ts143224 "CON%x full_feature(got-%d, expect-%d), errno=%d\n", 81 2314 mcneal c->c_num, cc, sizeof (h), errno); 82 2314 mcneal } 83 2314 mcneal queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug); 84 2314 mcneal conn_state(c, T8); 85 2314 mcneal return (False); 86 2314 mcneal } 87 2314 mcneal 88 2314 mcneal /* 89 2314 mcneal * Look to see if there's an Additional Header Segment available. 90 2314 mcneal * If so, read it in. 91 2314 mcneal */ 92 2314 mcneal if ((ahslen = (h.hlength * sizeof (uint32_t))) != 0) { 93 2314 mcneal if ((ahs = malloc(ahslen)) == NULL) 94 2314 mcneal return (False); 95 3022 mcneal if (recv(c->c_fd, ahs, ahslen, MSG_WAITALL) != ahslen) { 96 2314 mcneal (void) snprintf(debug, sizeof (debug), 97 2314 mcneal "CON%x Failed to read in AHS", c->c_num); 98 2314 mcneal queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug); 99 6086 ts143224 free(ahs); 100 2314 mcneal return (False); 101 2314 mcneal } 102 2314 mcneal } 103 2314 mcneal 104 7417 Tim (void) pthread_mutex_lock(&c->c_state_mutex); 105 7520 Tim if (c->c_state != S5_LOGGED_IN && c->c_state != S7_LOGOUT_REQUESTED) { 106 7417 Tim (void) snprintf(debug, sizeof (debug), 107 7417 Tim "CON%x full_feature -- not in S5_LOGGED_IN state\n", 108 7417 Tim c->c_num); 109 7417 Tim queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug); 110 7417 Tim if (ahs != NULL) 111 7417 Tim free(ahs); 112 7417 Tim (void) pthread_mutex_unlock(&c->c_state_mutex); 113 7417 Tim return (False); 114 7417 Tim } 115 7417 Tim (void) pthread_mutex_unlock(&c->c_state_mutex); 116 7417 Tim 117 7417 Tim if (c->c_header_digest == True) { 118 4568 ahl uint32_t crc_actual; 119 4568 ahl uint32_t crc_calculated; 120 2314 mcneal 121 3022 mcneal (void) recv(c->c_fd, (char *)&crc_actual, 122 3022 mcneal sizeof (crc_actual), MSG_WAITALL); 123 2314 mcneal crc_calculated = iscsi_crc32c((void *)&h, sizeof (h)); 124 2314 mcneal if (ahslen) 125 2314 mcneal crc_calculated = iscsi_crc32c_continued(ahs, 126 2314 mcneal ahslen, crc_calculated); 127 2314 mcneal if (crc_actual != crc_calculated) { 128 2314 mcneal (void) snprintf(debug, sizeof (debug), 129 2314 mcneal "CON%x CRC error: actual 0x%x v. calc 0x%x", 130 2314 mcneal c->c_num, crc_actual, crc_calculated); 131 2314 mcneal queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug); 132 6086 ts143224 if (ahs != NULL) 133 6086 ts143224 free(ahs); 134 2314 mcneal return (False); 135 2314 mcneal } 136 2314 mcneal } 137 2314 mcneal 138 2314 mcneal if (c->c_sess->s_type == SessionDiscovery) { 139 2314 mcneal switch (h.opcode & ISCSI_OPCODE_MASK) { 140 2314 mcneal default: 141 2314 mcneal /* 142 2314 mcneal * Need to handle the error case here. 143 2314 mcneal */ 144 2314 mcneal (void) snprintf(debug, sizeof (debug), 145 2314 mcneal "CON%x Wrong opcode for Discovery, %d", 146 2314 mcneal c->c_num, h.opcode & ISCSI_OPCODE_MASK); 147 2314 mcneal queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug); 148 2314 mcneal rval = False; 149 2314 mcneal break; 150 2314 mcneal 151 2314 mcneal case ISCSI_OP_LOGOUT_CMD: 152 2314 mcneal /* 153 2314 mcneal * This will transition from S5_LOGGED_IN 154 2314 mcneal * to S6_IN_LOGOUT to S1_FREE; 155 2314 mcneal */ 156 2314 mcneal rval = handle_logout_msg(c, &h, ahs, ahslen); 157 2314 mcneal break; 158 2314 mcneal 159 2314 mcneal case ISCSI_OP_TEXT_CMD: 160 2314 mcneal rval = handle_text_msg(c, &h, ahs, ahslen); 161 2314 mcneal break; 162 2314 mcneal } 163 2314 mcneal } else { 164 6086 ts143224 iscsi_cmd_remove(c, ntohl(h.expstatsn)); 165 2314 mcneal switch (h.opcode & ISCSI_OPCODE_MASK) { 166 2314 mcneal case ISCSI_OP_NOOP_OUT: 167 2314 mcneal rval = handle_noop_cmd(c, &h, ahs, ahslen); 168 2314 mcneal break; 169 2314 mcneal 170 2314 mcneal case ISCSI_OP_SCSI_CMD: 171 2314 mcneal rval = handle_scsi_cmd(c, &h, ahs, ahslen); 172 2314 mcneal break; 173 2314 mcneal 174 2314 mcneal case ISCSI_OP_SCSI_TASK_MGT_MSG: 175 2314 mcneal rval = handle_task_mgt(c, &h, ahs, ahslen); 176 2314 mcneal break; 177 2314 mcneal 178 2314 mcneal case ISCSI_OP_LOGIN_CMD: 179 2314 mcneal /* 180 2314 mcneal * This is an illegal state transition. Should 181 2314 mcneal * we drop the connection? 182 2314 mcneal */ 183 2314 mcneal break; 184 2314 mcneal 185 2314 mcneal case ISCSI_OP_TEXT_CMD: 186 2314 mcneal rval = handle_text_msg(c, &h, ahs, ahslen); 187 2314 mcneal break; 188 2314 mcneal 189 2314 mcneal case ISCSI_OP_SCSI_DATA: 190 2314 mcneal rval = handle_scsi_data(c, &h, ahs, ahslen); 191 2314 mcneal break; 192 2314 mcneal 193 2314 mcneal case ISCSI_OP_LOGOUT_CMD: 194 2314 mcneal /* 195 2314 mcneal * This will transition from S5_LOGGED_IN 196 2314 mcneal * to S6_IN_LOGOUT. 197 2314 mcneal */ 198 2314 mcneal rval = handle_logout_msg(c, &h, ahs, ahslen); 199 2314 mcneal break; 200 2314 mcneal 201 2314 mcneal case ISCSI_OP_SNACK_CMD: 202 2314 mcneal default: 203 2314 mcneal (void) snprintf(debug, sizeof (debug), 204 2314 mcneal "CON%x Opcode: %d not handled", 205 2314 mcneal c->c_num, h.opcode & ISCSI_OPCODE_MASK); 206 2314 mcneal queue_str(c->c_mgmtq, Q_CONN_ERRS, msg_log, debug); 207 2314 mcneal conn_state(c, T8); 208 2314 mcneal rval = True; 209 2314 mcneal break; 210 2314 mcneal } 211 2314 mcneal } 212 2314 mcneal 213 2314 mcneal if (ahs != NULL) 214 2314 mcneal free(ahs); 215 2314 mcneal return (rval); 216 2314 mcneal } 217 2314 mcneal 218 2314 mcneal /*ARGSUSED*/ 219 2314 mcneal static Boolean_t 220 2314 mcneal handle_task_mgt(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen) 221 2314 mcneal { 222 2314 mcneal iscsi_scsi_task_mgt_hdr_t *hp = (iscsi_scsi_task_mgt_hdr_t *)p; 223 2314 mcneal iscsi_scsi_task_mgt_rsp_hdr_t *rsp; 224 2314 mcneal iscsi_cmd_t *cmd; 225 2314 mcneal uint32_t lun; 226 2314 mcneal Boolean_t lu_reset = False; 227 2314 mcneal 228 4568 ahl if (spc_decode_lu_addr(&hp->lun[0], 8, &lun) == False) 229 2314 mcneal return (False); 230 2314 mcneal 231 4568 ahl if (ISCSI_TASK_COMMAND_ENABLED()) { 232 4568 ahl uiscsiproto_t info; 233 4568 ahl 234 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 235 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 236 4568 ahl 237 4568 ahl info.uip_target = c->c_sess->s_t_name; 238 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 239 4568 ahl info.uip_lun = lun; 240 4568 ahl 241 4568 ahl info.uip_itt = hp->itt; 242 4568 ahl info.uip_ttt = hp->itt; 243 4568 ahl 244 4568 ahl info.uip_cmdsn = ntohl(hp->cmdsn); 245 4568 ahl info.uip_statsn = ntohl(hp->expstatsn); 246 4568 ahl info.uip_datasn = ntohl(hp->expdatasn); 247 4568 ahl 248 4568 ahl info.uip_datalen = ntoh24(hp->dlength); 249 4568 ahl info.uip_flags = 0; 250 4568 ahl 251 4568 ahl ISCSI_TASK_COMMAND(&info); 252 4568 ahl } 253 4568 ahl 254 4568 ahl if ((rsp = calloc(1, sizeof (*rsp))) == NULL) 255 2314 mcneal return (False); 256 2314 mcneal 257 2314 mcneal rsp->opcode = ISCSI_OP_SCSI_TASK_MGT_RSP; 258 2314 mcneal rsp->flags = ISCSI_FLAG_FINAL; 259 2314 mcneal rsp->itt = hp->itt; 260 4822 ts143224 261 4822 ts143224 (void) pthread_mutex_lock(&c->c_mutex); 262 4822 ts143224 rsp->statsn = htonl(c->c_statsn++); 263 4822 ts143224 (void) pthread_mutex_unlock(&c->c_mutex); 264 2314 mcneal 265 2314 mcneal (void) pthread_mutex_lock(&c->c_sess->s_mutex); 266 2314 mcneal if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn) 267 2314 mcneal c->c_sess->s_seencmdsn = ntohl(hp->cmdsn); 268 2314 mcneal (void) pthread_mutex_unlock(&c->c_sess->s_mutex); 269 2314 mcneal 270 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_NONIO, 271 3077 mcneal "CON%x PDU(Task Mgt): %s, cmdsn 0x%x\n", 272 2314 mcneal c->c_num, 273 2314 mcneal task_to_str(hp->function & ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK), 274 2314 mcneal ntohl(hp->cmdsn)); 275 2314 mcneal 276 2314 mcneal switch (hp->function & ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK) { 277 2314 mcneal case ISCSI_TM_FUNC_ABORT_TASK: 278 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_NONIO, 279 3077 mcneal "CON%x Abort ITT 0x%x\n", c->c_num, hp->rtt); 280 2314 mcneal if ((cmd = iscsi_cmd_find(c, hp->rtt, FindITT)) == NULL) { 281 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 282 2314 mcneal "CON%x Invalid AbortTask rtt 0x%x\n", 283 2314 mcneal c->c_num, hp->rtt); 284 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_NO_TASK; 285 2314 mcneal } else { 286 6913 ts143224 (void) pthread_mutex_lock(&c->c_mutex); 287 2314 mcneal iscsi_cmd_cancel(c, cmd); 288 6913 ts143224 (void) pthread_mutex_unlock(&c->c_mutex); 289 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_COMPLETE; 290 2314 mcneal } 291 2314 mcneal break; 292 2314 mcneal 293 2314 mcneal case ISCSI_TM_FUNC_ABORT_TASK_SET: 294 2314 mcneal /* ---- This is actually "Function not support" ---- */ 295 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS; 296 2314 mcneal break; 297 2314 mcneal 298 2314 mcneal case ISCSI_TM_FUNC_CLEAR_ACA: 299 2314 mcneal /* ---- This is actually "Function not support" ---- */ 300 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS; 301 2314 mcneal break; 302 2314 mcneal 303 2314 mcneal case ISCSI_TM_FUNC_CLEAR_TASK_SET: 304 2314 mcneal /* ---- This is actually "Function not support" ---- */ 305 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS; 306 2314 mcneal break; 307 2314 mcneal 308 2314 mcneal case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: 309 2314 mcneal lu_reset = True; 310 2314 mcneal /*FALLTHRU*/ 311 2314 mcneal case ISCSI_TM_FUNC_TARGET_WARM_RESET: 312 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 313 2314 mcneal for (cmd = c->c_cmd_head; cmd; cmd = cmd->c_next) { 314 2314 mcneal if (((hp->function & 315 2314 mcneal ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK) == 316 2314 mcneal ISCSI_TM_FUNC_TARGET_WARM_RESET) || 317 2314 mcneal (lun == cmd->c_lun)) { 318 6913 ts143224 iscsi_cmd_cancel(c, cmd); 319 2314 mcneal 320 2314 mcneal } 321 2314 mcneal } 322 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 323 2314 mcneal 324 2314 mcneal if (lu_reset == True) 325 2314 mcneal queue_message_set(c->c_sessq, 0, msg_reset_lu, 326 2314 mcneal (void *)(uintptr_t)lun); 327 2314 mcneal else 328 2314 mcneal queue_message_set(c->c_sessq, 0, msg_reset_targ, 0); 329 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_COMPLETE; 330 2314 mcneal break; 331 2314 mcneal 332 2314 mcneal case ISCSI_TM_FUNC_TARGET_COLD_RESET: 333 2314 mcneal /* 334 2314 mcneal * According to the specification a cold reset should 335 2314 mcneal * close *all* connections on the target, not just those 336 2314 mcneal * for this current session. 337 2314 mcneal */ 338 2314 mcneal queue_message_set(c->c_sessq, 0, msg_reset_targ, (void *)1); 339 2314 mcneal conn_state(c, T8); 340 2314 mcneal break; 341 2314 mcneal 342 2314 mcneal case ISCSI_TM_FUNC_TASK_REASSIGN: 343 2314 mcneal default: 344 2314 mcneal /* ---- This is actually "Function not support" ---- */ 345 2314 mcneal rsp->response = SCSI_TCP_TM_RESP_IN_PRGRESS; 346 2314 mcneal break; 347 2314 mcneal } 348 2314 mcneal 349 2314 mcneal (void) pthread_mutex_lock(&c->c_state_mutex); 350 2314 mcneal if (c->c_state == S5_LOGGED_IN) 351 2314 mcneal queue_message_set(c->c_dataq, 352 2314 mcneal hp->opcode & ISCSI_OP_IMMEDIATE ? Q_HIGH : 0, 353 2314 mcneal msg_send_pkt, rsp); 354 2314 mcneal (void) pthread_mutex_unlock(&c->c_state_mutex); 355 2314 mcneal return (True); 356 2314 mcneal } 357 2314 mcneal 358 2314 mcneal /*ARGSUSED*/ 359 2314 mcneal static Boolean_t 360 2314 mcneal handle_noop_cmd(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen) 361 2314 mcneal { 362 2314 mcneal iscsi_nop_out_hdr_t *hp = (iscsi_nop_out_hdr_t *)p; 363 2314 mcneal iscsi_nop_in_hdr_t *in; 364 2314 mcneal 365 4568 ahl if (ISCSI_NOP_RECEIVE_ENABLED()) { 366 4568 ahl uiscsiproto_t info; 367 4568 ahl 368 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 369 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 370 4568 ahl 371 4568 ahl info.uip_target = c->c_sess->s_t_name; 372 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 373 4568 ahl info.uip_lun = 0; 374 4568 ahl 375 4568 ahl info.uip_itt = hp->itt; 376 4568 ahl info.uip_ttt = hp->ttt; 377 4568 ahl 378 4568 ahl info.uip_cmdsn = ntohl(hp->cmdsn); 379 4568 ahl info.uip_statsn = ntohl(hp->expstatsn); 380 4568 ahl info.uip_datasn = 0; 381 4568 ahl 382 4568 ahl info.uip_datalen = ntoh24(hp->dlength); 383 4568 ahl info.uip_flags = hp->flags; 384 4568 ahl 385 4568 ahl ISCSI_NOP_RECEIVE(&info); 386 4568 ahl } 387 4568 ahl 388 3545 mcneal /* 389 3545 mcneal * Just an answer to our ping 390 3545 mcneal */ 391 3545 mcneal if (hp->ttt != ISCSI_RSVD_TASK_TAG) 392 3545 mcneal return (True); 393 3545 mcneal 394 4568 ahl if ((in = calloc(1, sizeof (*in))) == NULL) { 395 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 396 4568 ahl "CON%x NopIn -- failed to malloc space for header", 397 2314 mcneal c->c_num); 398 2314 mcneal return (False); 399 2314 mcneal } 400 2314 mcneal 401 2314 mcneal in->opcode = ISCSI_OP_NOOP_IN; 402 2314 mcneal in->flags = ISCSI_FLAG_FINAL; 403 2314 mcneal /* 404 2314 mcneal * Need to handle possible data associated with NOP-Out 405 2314 mcneal */ 406 2314 mcneal bcopy(hp->lun, in->lun, 8); 407 2314 mcneal in->itt = hp->itt; 408 2314 mcneal in->ttt = ISCSI_RSVD_TASK_TAG; 409 2314 mcneal (void) pthread_mutex_lock(&c->c_sess->s_mutex); 410 2314 mcneal if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn) 411 2314 mcneal c->c_sess->s_seencmdsn = ntohl(hp->cmdsn); 412 2314 mcneal (void) pthread_mutex_unlock(&c->c_sess->s_mutex); 413 2314 mcneal 414 2314 mcneal (void) pthread_mutex_lock(&c->c_state_mutex); 415 2314 mcneal if (c->c_state == S5_LOGGED_IN) 416 2314 mcneal queue_message_set(c->c_dataq, 417 2314 mcneal hp->opcode & ISCSI_OP_IMMEDIATE ? Q_HIGH : 0, 418 2314 mcneal msg_send_pkt, in); 419 2314 mcneal (void) pthread_mutex_unlock(&c->c_state_mutex); 420 2314 mcneal return (True); 421 2314 mcneal } 422 2314 mcneal 423 2314 mcneal /*ARGSUSED*/ 424 2314 mcneal static Boolean_t 425 2314 mcneal handle_scsi_data(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen) 426 2314 mcneal { 427 2314 mcneal iscsi_data_hdr_t *hp = (iscsi_data_hdr_t *)p; 428 2314 mcneal int dlen = ntoh24(hp->dlength); 429 2314 mcneal iscsi_cmd_t *cmd; 430 4568 ahl 431 4568 ahl if (ISCSI_DATA_RECEIVE_ENABLED()) { 432 4568 ahl uiscsiproto_t info; 433 4568 ahl 434 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 435 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 436 4568 ahl 437 4568 ahl info.uip_target = c->c_sess->s_t_name; 438 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 439 4568 ahl info.uip_lun = 0; 440 4568 ahl 441 4568 ahl info.uip_itt = hp->itt; 442 4568 ahl info.uip_ttt = hp->itt; 443 4568 ahl 444 4568 ahl info.uip_cmdsn = 0; 445 4568 ahl info.uip_statsn = ntohl(hp->expstatsn); 446 4568 ahl info.uip_datasn = ntohl(hp->datasn); 447 4568 ahl 448 4568 ahl info.uip_datalen = dlen; 449 4568 ahl info.uip_flags = hp->flags; 450 4568 ahl 451 4568 ahl ISCSI_DATA_RECEIVE(&info); 452 4568 ahl } 453 2314 mcneal 454 2314 mcneal if ((cmd = iscsi_cmd_find(c, hp->ttt, FindTTT)) == NULL) { 455 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 456 3077 mcneal "CON%x failed to find ttt 0x%x\n", c->c_num, hp->ttt); 457 2314 mcneal /* 458 2314 mcneal * Need to handle error case. 459 2314 mcneal */ 460 2314 mcneal return (False); 461 2314 mcneal } 462 2314 mcneal cmd->c_opcode = hp->opcode & ISCSI_OPCODE_MASK; 463 2314 mcneal 464 2314 mcneal /* 465 2314 mcneal * assert(cmd->c_lun == hp->lun[1]); 466 2314 mcneal * Previously this check was done, but is caused a problem with 467 2314 mcneal * the RedHat initiator. There was a discussion on the IPS alias 468 2314 mcneal * around this very topic. Even though section 10.7.4 states: 469 2314 mcneal * "If the Target Transfer Tag is provided, then the LUN field 470 2314 mcneal * MUST hold a valid value and be consistent with whatever was 471 2314 mcneal * specified with the command; otherwise, the LUN field is 472 2314 mcneal * reserved." 473 2314 mcneal * Everyone agreed though that for a DataOut command the LUN field 474 2314 mcneal * wasn't required to be valid because the TTT gives the Target 475 2314 mcneal * enough information to complete the command. 476 2314 mcneal */ 477 2314 mcneal assert(cmd->c_allegiance == c); 478 2314 mcneal assert(cmd->c_itt == hp->itt); 479 2314 mcneal 480 2314 mcneal cmd->c_offset_out = ntohl(hp->offset); 481 2314 mcneal cmd->c_data_len = dlen; 482 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 483 2314 mcneal (void) pthread_mutex_lock(&c->c_state_mutex); 484 2314 mcneal if (c->c_state == S5_LOGGED_IN) { 485 3213 mcneal if (cmd->c_state != CmdCanceled) { 486 3077 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T4); 487 3213 mcneal } 488 2314 mcneal } 489 2314 mcneal (void) pthread_mutex_unlock(&c->c_state_mutex); 490 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 491 2314 mcneal 492 2314 mcneal #ifdef FULL_DEBUG 493 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_IO, 494 3077 mcneal "CON%x PDU(DataOut) TTT 0x%x, offset=0x%x, len=0x%x\n", 495 2314 mcneal c->c_num, cmd->c_ttt, cmd->c_t10_cmd->c_offset, dlen); 496 2314 mcneal #endif 497 2314 mcneal 498 2314 mcneal return (dataout_delayed(cmd, msg_cmd_data_out)); 499 2314 mcneal } 500 2314 mcneal 501 2314 mcneal static Boolean_t 502 2314 mcneal handle_scsi_cmd(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen) 503 2314 mcneal { 504 2314 mcneal iscsi_scsi_cmd_hdr_t *hp = (iscsi_scsi_cmd_hdr_t *)p; 505 2314 mcneal int dlen = ntoh24(hp->dlength); 506 2314 mcneal iscsi_cmd_t *cmd; 507 2314 mcneal 508 2314 mcneal (void) pthread_mutex_lock(&c->c_sess->s_mutex); 509 2314 mcneal if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn) 510 2314 mcneal c->c_sess->s_seencmdsn = ntohl(hp->cmdsn); 511 2314 mcneal (void) pthread_mutex_unlock(&c->c_sess->s_mutex); 512 2314 mcneal 513 2314 mcneal if ((cmd = iscsi_cmd_alloc(c, hp->opcode & ISCSI_OPCODE_MASK)) == NULL) 514 2314 mcneal return (False); 515 2314 mcneal 516 2314 mcneal bcopy(hp->scb, cmd->c_scb_default, sizeof (cmd->c_scb_default)); 517 2314 mcneal cmd->c_scb = cmd->c_scb_default; 518 2314 mcneal cmd->c_scb_len = sizeof (cmd->c_scb_default); 519 2314 mcneal cmd->c_data_len = dlen; 520 2314 mcneal 521 2314 mcneal if (ahslen) { 522 2314 mcneal 523 2314 mcneal /* 524 2314 mcneal * Additional Header Section ---- 525 2314 mcneal * 526 2314 mcneal * For Object Storage Devices the SCB is quite large. On 527 2314 mcneal * the order of 140 bytes which means the data must be 528 2314 mcneal * found in the AHS. 529 2314 mcneal */ 530 4568 ahl uint16_t hslen; 531 4568 ahl uint16_t next_seg; 532 2314 mcneal uint8_t hstyp; 533 2314 mcneal 534 2314 mcneal do { 535 2314 mcneal /* 536 2314 mcneal * Find this header segment's length and type 537 2314 mcneal */ 538 2314 mcneal bcopy(ahs, &hslen, sizeof (hslen)); 539 2314 mcneal hslen = ntohs(hslen); 540 2314 mcneal hstyp = ahs[2]; 541 2314 mcneal 542 2314 mcneal switch (hstyp) { 543 2314 mcneal /* ---- Extended CDB ---- */ 544 2314 mcneal case 1: 545 2314 mcneal /* 546 2314 mcneal * The hslen accounts for the reserved 547 2314 mcneal * data byte in the segment. So the first 548 2314 mcneal * sixteen bytes are in hp->scb with the 549 2314 mcneal * remainder here. By only adding 15 bytes 550 2314 mcneal * we allocate the correct amount of space 551 2314 mcneal */ 552 2314 mcneal cmd->c_scb_extended = malloc(hslen + 15); 553 2314 mcneal cmd->c_scb_len = hslen + 15; 554 2314 mcneal if (cmd->c_scb_extended == NULL) 555 2314 mcneal return (False); 556 2314 mcneal 557 2314 mcneal /* 558 2314 mcneal * First 16 bytes of extended SCB are 559 2314 mcneal * found in the normal location. 560 2314 mcneal */ 561 2314 mcneal bcopy(hp->scb, cmd->c_scb_extended, 16); 562 2314 mcneal bcopy(&ahs[4], &cmd->c_scb_extended[16], 563 2314 mcneal hslen - 16); 564 2314 mcneal cmd->c_scb = cmd->c_scb_extended; 565 2314 mcneal break; 566 2314 mcneal 567 2314 mcneal /* ---- Expected bidirectional read data len ---- */ 568 2314 mcneal case 2: 569 2314 mcneal /* 570 2314 mcneal * We shouldn't need this since we're 571 2314 mcneal * not prealloc'ing resources. If that should 572 2314 mcneal * change or the need for error checking 573 2314 mcneal * here's the spot to locate the data. 574 2314 mcneal */ 575 2314 mcneal break; 576 2314 mcneal } 577 2314 mcneal 578 2314 mcneal /* 579 2314 mcneal * hslen contains the effective length in bytes of 580 2314 mcneal * segment, excluding type and length (not including 581 2314 mcneal * padding). Each segment is padded to a 4 byte 582 2314 mcneal * boundary. 583 2314 mcneal */ 584 4568 ahl next_seg = ((hslen + sizeof (hslen) + 585 4568 ahl sizeof (hstyp) + 3) & ~3); 586 4568 ahl ahs += next_seg; 587 4568 ahl ahslen -= next_seg; 588 2314 mcneal 589 2314 mcneal } while (ahslen); 590 2314 mcneal } 591 2314 mcneal 592 2314 mcneal /* 593 2314 mcneal * XXX Need to handle error case better. 594 2314 mcneal */ 595 2314 mcneal if (spc_decode_lu_addr(&hp->lun[0], sizeof (hp->lun), &cmd->c_lun) == 596 2314 mcneal False) { 597 2314 mcneal return (False); 598 4568 ahl } 599 4568 ahl 600 4568 ahl if (ISCSI_SCSI_COMMAND_ENABLED()) { 601 4568 ahl uiscsiproto_t info; 602 4568 ahl uiscsicmd_t uc; 603 4568 ahl 604 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 605 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 606 4568 ahl 607 4568 ahl info.uip_target = c->c_sess->s_t_name; 608 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 609 4568 ahl info.uip_lun = cmd->c_lun; 610 4568 ahl 611 4568 ahl info.uip_itt = hp->itt; 612 4568 ahl info.uip_ttt = ISCSI_RSVD_TASK_TAG; 613 4568 ahl 614 4568 ahl info.uip_cmdsn = ntohl(hp->cmdsn); 615 4568 ahl info.uip_statsn = ntohl(hp->expstatsn); 616 4568 ahl info.uip_datasn = 0; 617 4568 ahl 618 4568 ahl info.uip_datalen = dlen; 619 4568 ahl info.uip_flags = hp->flags; 620 4568 ahl 621 4568 ahl uc.uic_len = cmd->c_scb_len; 622 4568 ahl uc.uic_cdb = cmd->c_scb; 623 4568 ahl 624 4568 ahl ISCSI_SCSI_COMMAND(&info, &uc); 625 2314 mcneal } 626 2314 mcneal 627 2314 mcneal cmd->c_itt = hp->itt; 628 2314 mcneal cmd->c_cmdsn = ntohl(hp->cmdsn); 629 3452 mcneal cmd->c_dlen_expected = ntohl(hp->data_length); 630 2314 mcneal cmd->c_writeop = hp->flags & ISCSI_FLAG_CMD_WRITE ? 631 2314 mcneal True : False; 632 2314 mcneal 633 2314 mcneal #ifdef FULL_DEBUG 634 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_IO, 635 3213 mcneal "CON%x PDU(SCSI Cmd, TA=%d) CmdSN 0x%x ITT 0x%x TTT 0x%x " 636 3213 mcneal "LUN[%02x] id=%p\n", c->c_num, 637 3213 mcneal hp->flags & ISCSI_FLAG_CMD_ATTR_MASK, cmd->c_cmdsn, cmd->c_itt, 638 3213 mcneal cmd->c_ttt, cmd->c_lun, cmd); 639 2314 mcneal #endif 640 3213 mcneal 641 2314 mcneal if (dlen && (hp->flags & ISCSI_FLAG_CMD_WRITE)) { 642 2314 mcneal /* 643 2314 mcneal * NOTE: This should only occur if ImmediateData==Yes. 644 2314 mcneal * We can handle this even if the initiator violates 645 2314 mcneal * the specification so no need to worry. Use the rule 646 2314 mcneal * of "Be strict in what is sent, but lenient in what 647 2314 mcneal * is accepted." 648 2314 mcneal */ 649 2314 mcneal return (dataout_delayed(cmd, msg_cmd_send)); 650 2314 mcneal } else { 651 2314 mcneal (void) pthread_mutex_lock(&c->c_state_mutex); 652 2314 mcneal if (c->c_state == S5_LOGGED_IN) 653 2314 mcneal queue_message_set(c->c_sessq, 0, 654 2314 mcneal msg_cmd_send, (void *)cmd); 655 2314 mcneal (void) pthread_mutex_unlock(&c->c_state_mutex); 656 2314 mcneal return (True); 657 2314 mcneal } 658 2314 mcneal } 659 2314 mcneal 660 2314 mcneal /* 661 2314 mcneal * []---- 662 2314 mcneal * | handle_text_msg -- process incoming test parameters 663 2314 mcneal * | 664 2314 mcneal * | NOTE: Need to handle continuation packets sent by the initiator. 665 2314 mcneal * []---- 666 2314 mcneal */ 667 2314 mcneal /*ARGSUSED*/ 668 2314 mcneal static Boolean_t 669 2314 mcneal handle_text_msg(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen) 670 2314 mcneal { 671 2314 mcneal iscsi_text_rsp_hdr_t rsp; 672 2314 mcneal iscsi_text_hdr_t *hp = (iscsi_text_hdr_t *)p; 673 2314 mcneal char *text = NULL; 674 2314 mcneal int text_length = 0; 675 2314 mcneal Boolean_t release_at_end = False; 676 4568 ahl int dlen = ntoh24(hp->dlength); 677 4568 ahl 678 4568 ahl if (ISCSI_TEXT_COMMAND_ENABLED()) { 679 4568 ahl uiscsiproto_t info; 680 4568 ahl char nil = '\0'; 681 4568 ahl 682 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 683 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 684 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 685 4568 ahl 686 4568 ahl info.uip_target = c->c_sess->s_t_name; 687 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 688 4568 ahl info.uip_target = &nil; 689 4568 ahl info.uip_lun = 0; 690 4568 ahl 691 4568 ahl info.uip_itt = hp->itt; 692 4568 ahl info.uip_ttt = hp->ttt; 693 4568 ahl 694 4568 ahl info.uip_cmdsn = ntohl(hp->cmdsn); 695 4568 ahl info.uip_statsn = ntohl(hp->expstatsn); 696 4568 ahl info.uip_datasn = 0; 697 4568 ahl 698 4568 ahl info.uip_datalen = dlen; 699 4568 ahl info.uip_flags = hp->flags; 700 4568 ahl 701 4568 ahl ISCSI_TEXT_COMMAND(&info); 702 4568 ahl } 703 2314 mcneal 704 2314 mcneal bzero(&rsp, sizeof (rsp)); 705 2314 mcneal rsp.opcode = ISCSI_OP_TEXT_RSP; 706 2314 mcneal rsp.itt = hp->itt; 707 2314 mcneal 708 3077 mcneal queue_prt(c->c_mgmtq, Q_CONN_NONIO, "CON%x PDU(Text Message)\n", 709 4568 ahl c->c_num); 710 2314 mcneal 711 2314 mcneal /* 712 2314 mcneal * Need to determine if this incoming text PDU is an initial message 713 2314 mcneal * or a continuation. 714 2314 mcneal */ 715 2314 mcneal if (hp->ttt == ISCSI_RSVD_TASK_TAG) { 716 2314 mcneal 717 2314 mcneal /* ---- Initial text PDU, so parse the incoming data ---- */ 718 4568 ahl if (parse_text(c, dlen, &text, &text_length, NULL) == False) { 719 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 720 3077 mcneal "Failed to parse Text\n"); 721 2314 mcneal if (text) { 722 2314 mcneal /* 723 2314 mcneal * It's possible that we started to create 724 2314 mcneal * a response, but yet an error occurred. 725 2314 mcneal * Release the partial text response if that 726 2314 mcneal * occurred. 727 2314 mcneal */ 728 2314 mcneal free(text); 729 2314 mcneal } 730 2314 mcneal return (False); 731 2314 mcneal } 732 2314 mcneal 733 2314 mcneal /* 734 2314 mcneal * 10.11.4 -- 735 2314 mcneal * When the target receives a Text Request with the Target 736 2314 mcneal * Transfer Tag set to the reserved value of 0xffffffff, it 737 2314 mcneal * resets its internal information (resets state) associated 738 2314 mcneal * with the given Initiator Task Tag (restarts the negotiation). 739 2314 mcneal */ 740 2314 mcneal if (c->c_text_area != NULL) 741 2314 mcneal free(c->c_text_area); 742 2314 mcneal 743 2314 mcneal c->c_text_area = text; 744 2314 mcneal if (text_length > c->c_max_recv_data) { 745 2314 mcneal 746 2314 mcneal /* 747 2314 mcneal * Too much data to send at once, break it up into 748 2314 mcneal * multiple transfers. 749 2314 mcneal */ 750 2314 mcneal rsp.flags = ISCSI_FLAG_TEXT_CONTINUE; 751 2314 mcneal rsp.ttt = 1; 752 2314 mcneal c->c_text_len = text_length; 753 2314 mcneal text_length = c->c_max_recv_data; 754 2314 mcneal c->c_text_sent = text_length; 755 2314 mcneal } else { 756 2314 mcneal rsp.flags = ISCSI_FLAG_FINAL; 757 2314 mcneal rsp.ttt = ISCSI_RSVD_TASK_TAG; 758 2314 mcneal release_at_end = True; 759 2314 mcneal } 760 2314 mcneal } else { 761 2314 mcneal 762 2314 mcneal /* ---- Continuation of previous text request ---- */ 763 2314 mcneal text_length = c->c_text_len - c->c_text_sent; 764 2314 mcneal text = c->c_text_area + c->c_text_sent; 765 2314 mcneal if (text_length > c->c_max_recv_data) { 766 2314 mcneal rsp.flags = ISCSI_FLAG_TEXT_CONTINUE; 767 2314 mcneal rsp.ttt = 1; 768 2314 mcneal text_length = c->c_max_recv_data; 769 2314 mcneal c->c_text_sent += text_length; 770 2314 mcneal } else { 771 2314 mcneal rsp.flags = ISCSI_FLAG_FINAL; 772 2314 mcneal rsp.ttt = ISCSI_RSVD_TASK_TAG; 773 2314 mcneal release_at_end = True; 774 2314 mcneal } 775 2314 mcneal } 776 2314 mcneal 777 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_NONIO, 778 3077 mcneal "CON%x Text PDU: flags=0x%02x, ttt=0x%08x, len=%d\n", 779 2314 mcneal c->c_num, rsp.flags, rsp.ttt, text_length); 780 2314 mcneal 781 2314 mcneal hton24(rsp.dlength, text_length); 782 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 783 2314 mcneal rsp.statsn = htonl(c->c_statsn++); 784 2314 mcneal (void) pthread_mutex_lock(&c->c_sess->s_mutex); 785 2314 mcneal if (ntohl(hp->cmdsn) > c->c_sess->s_seencmdsn) 786 2314 mcneal c->c_sess->s_seencmdsn = ntohl(hp->cmdsn); 787 2314 mcneal rsp.maxcmdsn = htonl(iscsi_cmd_window(c) + c->c_sess->s_seencmdsn); 788 2314 mcneal rsp.expcmdsn = htonl(c->c_sess->s_seencmdsn + 1); 789 2314 mcneal (void) pthread_mutex_unlock(&c->c_sess->s_mutex); 790 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 791 2314 mcneal 792 4568 ahl if (ISCSI_TEXT_RESPONSE_ENABLED()) { 793 4568 ahl uiscsiproto_t info; 794 4568 ahl char nil = '\0'; 795 4568 ahl 796 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 797 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 798 4568 ahl 799 4568 ahl info.uip_target = c->c_sess->s_t_name; 800 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 801 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 802 4568 ahl info.uip_target = &nil; 803 4568 ahl info.uip_lun = 0; 804 4568 ahl 805 4568 ahl info.uip_itt = rsp.itt; 806 4568 ahl info.uip_ttt = rsp.ttt; 807 4568 ahl 808 4568 ahl info.uip_cmdsn = ntohl(rsp.expcmdsn); 809 4568 ahl info.uip_statsn = ntohl(rsp.statsn); 810 4568 ahl info.uip_datasn = 0; 811 4568 ahl 812 4568 ahl info.uip_datalen = text_length; 813 4568 ahl info.uip_flags = rsp.flags; 814 4568 ahl 815 4568 ahl ISCSI_TEXT_RESPONSE(&info); 816 4568 ahl } 817 4568 ahl 818 2314 mcneal send_iscsi_pkt(c, (iscsi_hdr_t *)&rsp, text); 819 2314 mcneal 820 2314 mcneal if (release_at_end == True) { 821 2314 mcneal free(c->c_text_area); 822 2314 mcneal c->c_text_area = NULL; 823 2314 mcneal } 824 2314 mcneal return (True); 825 2314 mcneal } 826 2314 mcneal 827 2314 mcneal /*ARGSUSED*/ 828 2314 mcneal static Boolean_t 829 2314 mcneal handle_logout_msg(iscsi_conn_t *c, iscsi_hdr_t *p, char *ahs, int ahslen) 830 2314 mcneal { 831 2314 mcneal iscsi_logout_rsp_hdr_t *rsp; 832 2314 mcneal iscsi_logout_hdr_t *hp = (iscsi_logout_hdr_t *)p; 833 2314 mcneal char debug[80]; 834 2314 mcneal 835 4568 ahl if (ISCSI_LOGOUT_COMMAND_ENABLED()) { 836 4568 ahl uiscsiproto_t info; 837 4568 ahl char nil = '\0'; 838 2314 mcneal 839 4568 ahl info.uip_target_addr = &c->c_target_sockaddr; 840 4568 ahl info.uip_initiator_addr = &c->c_initiator_sockaddr; 841 2314 mcneal 842 4568 ahl info.uip_target = c->c_sess->s_t_name; 843 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 844 4568 ahl info.uip_initiator = c->c_sess->s_i_name; 845 4568 ahl info.uip_target = &nil; 846 4568 ahl info.uip_lun = 0; 847 2314 mcneal 848 4568 ahl info.uip_itt = hp->itt; 849 4568 ahl info.uip_ttt = ISCSI_RSVD_TASK_TAG; 850 2314 mcneal 851 4568 ahl info.uip_cmdsn = ntohl(hp->cmdsn); 852 4568 ahl info.uip_statsn = ntohl(hp->expstatsn); 853 4568 ahl info.uip_datasn = 0; 854 2314 mcneal 855 4568 ahl info.uip_datalen = ntoh24(hp->dlength); 856 4568 ahl info.uip_flags = hp->flags; 857 2314 mcneal 858 4568 ahl ISCSI_LOGOUT_COMMAND(&info); 859 4568 ahl } 860 4568 ahl 861 4568 ahl if ((rsp = calloc(1, sizeof (*rsp))) == NULL) 862 2314 mcneal return (False); 863 4568 ahl 864 4568 ahl (void) snprintf(debug, sizeof (debug), 865 4568 ahl "CON%x PDU(Logout Request)", c->c_num); 866 4568 ahl queue_str(c->c_mgmtq, Q_CONN_NONIO, msg_log, debug); 867 4568 ahl 868 4568 ahl (void) pthread_mutex_lock(&c->c_mutex); 869 4568 ahl (void) pthread_mutex_lock(&c->c_sess->s_mutex); 870 4568 ahl if (hp->cmdsn > c->c_sess->s_seencmdsn) 871 4568 ahl c->c_sess->s_seencmdsn = htonl(hp->cmdsn); 872 4568 ahl rsp->expcmdsn = htonl(c->c_sess->s_seencmdsn + 1); 873 4568 ahl rsp->maxcmdsn = htonl(iscsi_cmd_window(c) + 874 4568 ahl c->c_sess->s_seencmdsn); 875 4568 ahl (void) pthread_mutex_unlock(&c->c_sess->s_mutex); 876 4568 ahl (void) pthread_mutex_unlock(&c->c_mutex); 877 4568 ahl 878 4568 ahl rsp->opcode = ISCSI_OP_LOGOUT_RSP; 879 4568 ahl rsp->flags = ISCSI_FLAG_FINAL; 880 4568 ahl rsp->itt = hp->itt; 881 4568 ahl (void) pthread_mutex_lock(&c->c_mutex); 882 4568 ahl rsp->statsn = htonl(c->c_statsn++); 883 4568 ahl (void) pthread_mutex_unlock(&c->c_mutex); 884 4568 ahl 885 4568 ahl c->c_last_pkg = (iscsi_hdr_t *)rsp; 886 4568 ahl 887 4568 ahl /* 888 4568 ahl * Call the state transition last. This will send out 889 4568 ahl * an asynchronous message to shutdown the session and STE. 890 4568 ahl * Once that's complete a shutdown reply will be sent to 891 4568 ahl * the transmit connection thread. That will cause another 892 4568 ahl * transition to T13 which expects to send out this logout 893 4568 ahl * response. 894 4568 ahl */ 895 4568 ahl if (c->c_state == S7_LOGOUT_REQUESTED) 896 4568 ahl conn_state(c, T10); 897 4568 ahl else 898 4568 ahl conn_state(c, T9); 899 4568 ahl 900 4568 ahl return (True); 901 2314 mcneal } 902 2314 mcneal 903 2314 mcneal /* 904 3077 mcneal * dataout_delayed -- possibly copy data from initiator 905 2314 mcneal * 906 2314 mcneal * If DataDigests are enabled copy the data from the socket into a buffer 907 2314 mcneal * and perform the CRC check now. 908 2314 mcneal * 909 2314 mcneal * If MaxConnections==1 don't copy the data now and wait until the STE is 910 2314 mcneal * ready to copy the data directly from the socket to it's final location. 911 2314 mcneal * This is extremely beneficial when using mmap'd data. 912 2314 mcneal * NOTE: 913 2314 mcneal * (1) For this to work we must not use the queues and instead 914 2314 mcneal * call the STE functions directly. If the queues are used 915 2314 mcneal * this routine must pause until STE processes the data to 916 2314 mcneal * prevent this thread from attempting to read data from 917 2314 mcneal * the socket as if it's the next PDU header. 918 2314 mcneal * (2) Currently we don't call STE directly. To prevent a performance 919 2314 mcneal * issue we'll have the code in place to support calling 920 2314 mcneal * STE directly, but any time MaxConnections is greater than 0 921 2314 mcneal * we'll copy the buffer. This will be removed at some future 922 2314 mcneal * point. 923 2314 mcneal */ 924 2314 mcneal static Boolean_t 925 2314 mcneal dataout_delayed(iscsi_cmd_t *cmd, msg_type_t type) 926 2314 mcneal { 927 2314 mcneal iscsi_conn_t *c = cmd->c_allegiance; 928 4568 ahl int dlen = cmd->c_data_len; 929 4568 ahl int cc; 930 4568 ahl uint32_t crc_calc; 931 4568 ahl uint32_t crc_actual; 932 2314 mcneal char pad_buf[ISCSI_PAD_WORD_LEN - 1]; 933 2314 mcneal char pad_len; 934 2314 mcneal char debug[80]; 935 2314 mcneal 936 2314 mcneal cmd->c_dataout_cb = dataout_callback; 937 2314 mcneal 938 2314 mcneal if (cmd->c_data == NULL) { 939 2314 mcneal if ((cmd->c_data = (char *)malloc(dlen)) == NULL) { 940 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 941 3213 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 942 2314 mcneal iscsi_cmd_free(c, cmd); 943 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 944 2314 mcneal return (False); 945 2314 mcneal } 946 2314 mcneal cmd->c_data_alloc = True; 947 2314 mcneal } 948 2314 mcneal 949 3022 mcneal if ((cc = recv(c->c_fd, cmd->c_data, dlen, MSG_WAITALL)) != dlen) { 950 2314 mcneal if (errno == ECONNRESET) { 951 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 952 2314 mcneal "CON%x dataout_delayed -- " 953 3077 mcneal "initiator reset socket\n", c->c_num); 954 2314 mcneal } else { 955 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 956 3077 mcneal "CON%x recv(got-%d, expect-%d), errno=%d\n", 957 3022 mcneal c->c_num, cc, dlen, errno); 958 2314 mcneal } 959 2314 mcneal 960 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 961 3213 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 962 2314 mcneal iscsi_cmd_free(c, cmd); 963 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 964 2314 mcneal conn_state(c, T8); 965 2314 mcneal return (True); 966 2314 mcneal } 967 2314 mcneal 968 2314 mcneal pad_len = ((ISCSI_PAD_WORD_LEN - 969 2314 mcneal (dlen & (ISCSI_PAD_WORD_LEN - 1))) & (ISCSI_PAD_WORD_LEN - 1)); 970 2314 mcneal 971 2314 mcneal if (pad_len) { 972 3022 mcneal if (recv(c->c_fd, pad_buf, pad_len, MSG_WAITALL) != pad_len) { 973 2314 mcneal if (errno == ECONNRESET) { 974 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 975 2314 mcneal "CON%x dataout_delayed -- " 976 3077 mcneal "initiator reset socket\n", c->c_num); 977 2314 mcneal } else { 978 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 979 3077 mcneal "CON%x Pad Word read errno=%d\n", c->c_num, 980 2314 mcneal errno); 981 2314 mcneal } 982 2314 mcneal 983 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 984 3213 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 985 2314 mcneal iscsi_cmd_free(c, cmd); 986 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 987 2314 mcneal conn_state(c, T8); 988 2314 mcneal return (True); 989 2314 mcneal } 990 2314 mcneal } 991 2314 mcneal 992 2314 mcneal if (c->c_data_digest == True) { 993 3022 mcneal if (recv(c->c_fd, (char *)&crc_actual, sizeof (crc_actual), 994 3022 mcneal MSG_WAITALL) != sizeof (crc_actual)) { 995 2314 mcneal if (errno == ECONNRESET) { 996 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 997 2314 mcneal "CON%x dataout_delayed -- " 998 3077 mcneal "initiator reset socket\n", c->c_num); 999 2314 mcneal } else { 1000 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 1001 3077 mcneal "CON%x CRC32 read errno=%d\n", c->c_num, 1002 2314 mcneal errno); 1003 2314 mcneal } 1004 2314 mcneal 1005 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 1006 3213 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 1007 2314 mcneal iscsi_cmd_free(c, cmd); 1008 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 1009 2314 mcneal conn_state(c, T8); 1010 2314 mcneal return (True); 1011 2314 mcneal } 1012 2314 mcneal crc_calc = iscsi_crc32c((void *)cmd->c_data, dlen); 1013 2314 mcneal if (crc_calc != crc_actual) { 1014 2314 mcneal 1015 2314 mcneal (void) snprintf(debug, sizeof (debug), 1016 2314 mcneal "CON%x CRC Error: actual %x vs. calc 0x%x", 1017 2314 mcneal c->c_num, crc_actual, crc_calc); 1018 2314 mcneal 1019 2314 mcneal /* 1020 2314 mcneal * NOTE: Need to think about this one some more. 1021 2314 mcneal * Just because we get a data error doesn't mean 1022 2314 mcneal * we should drop the connection. Look at the 1023 2314 mcneal * spec and determine what's the appropriate 1024 2314 mcneal * error recovery for this issue. 1025 2314 mcneal */ 1026 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 1027 3213 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 1028 2314 mcneal iscsi_cmd_free(c, cmd); 1029 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 1030 2314 mcneal conn_state(c, T8); 1031 2314 mcneal return (True); 1032 2314 mcneal } 1033 2314 mcneal } 1034 2314 mcneal 1035 3452 mcneal /* 1036 3452 mcneal * We'll update the offset with the amount of data that 1037 3452 mcneal * has been received. During a SCSI response PDU this value 1038 3452 mcneal * will be used to determine if there's an overrun condition. 1039 3452 mcneal */ 1040 3452 mcneal cmd->c_offset_out += dlen; 1041 3452 mcneal 1042 2314 mcneal (void) pthread_mutex_lock(&c->c_mutex); 1043 2314 mcneal (void) pthread_mutex_lock(&c->c_state_mutex); 1044 2314 mcneal if (c->c_state == S5_LOGGED_IN) { 1045 2314 mcneal if ((cmd->c_state == CmdCanceled) && 1046 3213 mcneal (type == msg_cmd_data_out)) 1047 3077 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 1048 3213 mcneal else 1049 2314 mcneal queue_message_set(c->c_sessq, 0, type, (void *)cmd); 1050 2314 mcneal } else if (cmd->c_state == CmdCanceled) { 1051 3077 mcneal t10_cmd_shoot_event(cmd->c_t10_cmd, T10_Cmd_T5); 1052 2314 mcneal } 1053 2314 mcneal (void) pthread_mutex_unlock(&c->c_state_mutex); 1054 2314 mcneal (void) pthread_mutex_unlock(&c->c_mutex); 1055 2314 mcneal 1056 2314 mcneal /* 1057 2314 mcneal * The else case here is if we're calling STE directly and the data 1058 2314 mcneal * will be read from the socket when STE is ready for it. 1059 2314 mcneal */ 1060 2314 mcneal 1061 2314 mcneal return (True); 1062 2314 mcneal } 1063 2314 mcneal 1064 2314 mcneal /* 1065 2314 mcneal * []---- 1066 2314 mcneal * | dataout_callback -- copy data from socket to emulation buffer 1067 2314 mcneal * []---- 1068 2314 mcneal */ 1069 2314 mcneal void 1070 2314 mcneal dataout_callback(t10_cmd_t *t, char *data, size_t *xfer) 1071 2314 mcneal { 1072 2314 mcneal iscsi_cmd_t *cmd = (iscsi_cmd_t *)T10_TRANS_ID(t); 1073 2314 mcneal iscsi_conn_t *c = cmd->c_allegiance; 1074 4568 ahl int dlen = cmd->c_data_len; 1075 4568 ahl int cc; 1076 2314 mcneal char pad_buf[ISCSI_PAD_WORD_LEN - 1]; 1077 2314 mcneal char pad_len = 0; 1078 2314 mcneal 1079 2314 mcneal pad_len = ((ISCSI_PAD_WORD_LEN - 1080 2314 mcneal (dlen & (ISCSI_PAD_WORD_LEN - 1))) & 1081 2314 mcneal (ISCSI_PAD_WORD_LEN - 1)); 1082 2314 mcneal 1083 2314 mcneal 1084 2314 mcneal if (T10_DATA(t) != NULL) { 1085 2314 mcneal assert(T10_DATA(t) == cmd->c_data); 1086 2314 mcneal assert(cmd->c_data_alloc == True); 1087 2314 mcneal free(T10_DATA(t)); 1088 2314 mcneal T10_DATA(t) = NULL; 1089 2314 mcneal cmd->c_data = NULL; 1090 2314 mcneal cmd->c_data_alloc = False; 1091 2314 mcneal return; 1092 2314 mcneal } 1093 2314 mcneal 1094 3022 mcneal if ((cc = recv(c->c_fd, data, dlen, MSG_WAITALL)) != dlen) { 1095 2314 mcneal if (errno == ECONNRESET) { 1096 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 1097 3077 mcneal "CON%x data_callback -- initiator reset socket\n", 1098 2314 mcneal c->c_num); 1099 2314 mcneal } else { 1100 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 1101 3022 mcneal "CON%x recv(got-%d, expect-%d) errno=%d", 1102 2314 mcneal c->c_num, cc, dlen, errno); 1103 2314 mcneal } 1104 2314 mcneal 1105 2314 mcneal conn_state(c, T8); 1106 2314 mcneal goto finish; 1107 2314 mcneal } 1108 2314 mcneal 1109 2314 mcneal if (pad_len) { 1110 3022 mcneal if (recv(c->c_fd, pad_buf, pad_len, MSG_WAITALL) != pad_len) { 1111 2314 mcneal if (errno == ECONNRESET) { 1112 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 1113 2314 mcneal "CON%x data_callback -- " 1114 3077 mcneal "initiator reset socket\n", c->c_num); 1115 2314 mcneal } else { 1116 2314 mcneal queue_prt(c->c_mgmtq, Q_CONN_ERRS, 1117 2314 mcneal "CON%x data_callback -- " 1118 3077 mcneal "pad read errno=%d\n", c->c_num, errno); 1119 2314 mcneal } 1120 2314 mcneal conn_state(c, T8); 1121 2314 mcneal goto finish; 1122 2314 mcneal } 1123 2314 mcneal } 1124 2314 mcneal 1125 2314 mcneal finish: 1126 2314 mcneal *xfer = cc; 1127 2314 mcneal /* ---- Send msg that receive side of the connection can go ---- */ 1128 2314 mcneal (void) sema_post(&c->c_datain); 1129 2314 mcneal } 1130