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 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/cpuvar.h> 26 #include <sys/types.h> 27 #include <sys/conf.h> 28 #include <sys/file.h> 29 #include <sys/ddi.h> 30 #include <sys/sunddi.h> 31 #include <sys/modctl.h> 32 #include <sys/sysmacros.h> 33 34 #include <sys/socket.h> 35 #include <sys/strsubr.h> 36 #include <sys/door.h> 37 38 #include <sys/stmf.h> 39 #include <sys/stmf_ioctl.h> 40 #include <sys/portif.h> 41 42 #include "pppt.h" 43 44 static void pppt_msg_tgt_register(stmf_ic_msg_t *reg_port); 45 46 static void pppt_msg_tgt_deregister(stmf_ic_msg_t *msg); 47 48 static void pppt_msg_session_destroy(stmf_ic_msg_t *msg); 49 50 static void pppt_msg_scsi_cmd(stmf_ic_msg_t *msg); 51 52 static void pppt_msg_data_xfer_done(stmf_ic_msg_t *msg); 53 54 static void pppt_msg_handle_status(stmf_ic_msg_t *msg); 55 56 void 57 pppt_msg_rx(stmf_ic_msg_t *msg) 58 { 59 switch (msg->icm_msg_type) { 60 case STMF_ICM_REGISTER_PROXY_PORT: 61 pppt_msg_tgt_register(msg); 62 break; 63 case STMF_ICM_DEREGISTER_PROXY_PORT: 64 pppt_msg_tgt_deregister(msg); 65 break; 66 case STMF_ICM_SESSION_CREATE: 67 pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED); 68 stmf_ic_msg_free(msg); 69 break; 70 case STMF_ICM_SESSION_DESTROY: 71 pppt_msg_session_destroy(msg); 72 break; 73 case STMF_ICM_SCSI_CMD: 74 pppt_msg_scsi_cmd(msg); 75 break; 76 case STMF_ICM_SCSI_DATA_XFER_DONE: 77 pppt_msg_data_xfer_done(msg); 78 break; 79 case STMF_ICM_SCSI_DATA: 80 /* Ignore, all proxy data will be immediate for now */ 81 pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED); 82 stmf_ic_msg_free(msg); 83 break; 84 case STMF_ICM_STATUS: 85 pppt_msg_handle_status(msg); 86 break; 87 default: 88 /* Other message types are not allowed */ 89 ASSERT(0); 90 break; 91 } 92 } 93 94 void 95 pppt_msg_tx_status(stmf_ic_msg_t *orig_msg, stmf_status_t status) 96 { 97 stmf_ic_msg_t *msg; 98 99 /* 100 * If TX of status fails it should be treated the same as a loss of 101 * connection. We expect the remote node to handle it. 102 */ 103 msg = stmf_ic_status_msg_alloc(status, orig_msg->icm_msg_type, 104 orig_msg->icm_msgid); 105 106 if (msg != NULL) { 107 (void) stmf_ic_tx_msg(msg); 108 } 109 } 110 111 static void 112 pppt_msg_tgt_register(stmf_ic_msg_t *msg) 113 { 114 stmf_ic_reg_port_msg_t *reg_port; 115 pppt_tgt_t *result; 116 stmf_status_t stmf_status; 117 118 reg_port = msg->icm_msg; 119 120 PPPT_GLOBAL_LOCK(); 121 if (pppt_global.global_svc_state != PSS_ENABLED) { 122 stmf_status = STMF_FAILURE; 123 PPPT_INC_STAT(es_tgt_reg_svc_disabled); 124 goto pppt_register_tgt_done; 125 } 126 127 /* 128 * For now we assume that the marshall/unmarshall code is responsible 129 * for validating the message length and ensuring the resulting 130 * request structure is self consistent. Make sure this 131 * target doesn't already exist. 132 */ 133 if ((result = pppt_tgt_lookup_locked(reg_port->icrp_port_id)) != NULL) { 134 stmf_status = STMF_ALREADY; 135 PPPT_INC_STAT(es_tgt_reg_duplicate); 136 goto pppt_register_tgt_done; 137 } 138 139 result = pppt_tgt_create(reg_port, &stmf_status); 140 141 if (result == NULL) { 142 stmf_status = STMF_TARGET_FAILURE; 143 PPPT_INC_STAT(es_tgt_reg_create_fail); 144 goto pppt_register_tgt_done; 145 } 146 147 avl_add(&pppt_global.global_target_list, result); 148 149 stmf_status = STMF_SUCCESS; 150 151 pppt_register_tgt_done: 152 PPPT_GLOBAL_UNLOCK(); 153 pppt_msg_tx_status(msg, stmf_status); 154 stmf_ic_msg_free(msg); 155 } 156 157 static void 158 pppt_msg_tgt_deregister(stmf_ic_msg_t *msg) 159 { 160 stmf_ic_dereg_port_msg_t *dereg_port; 161 stmf_status_t stmf_status; 162 pppt_tgt_t *tgt; 163 164 PPPT_GLOBAL_LOCK(); 165 if (pppt_global.global_svc_state != PSS_ENABLED) { 166 PPPT_GLOBAL_UNLOCK(); 167 stmf_status = STMF_FAILURE; 168 PPPT_INC_STAT(es_tgt_dereg_svc_disabled); 169 goto pppt_deregister_tgt_done; 170 } 171 172 dereg_port = msg->icm_msg; 173 174 /* Lookup target */ 175 if ((tgt = pppt_tgt_lookup_locked(dereg_port->icdp_port_id)) == NULL) { 176 PPPT_GLOBAL_UNLOCK(); 177 stmf_status = STMF_NOT_FOUND; 178 PPPT_INC_STAT(es_tgt_dereg_not_found); 179 goto pppt_deregister_tgt_done; 180 } 181 avl_remove(&pppt_global.global_target_list, tgt); 182 pppt_tgt_async_delete(tgt); 183 184 PPPT_GLOBAL_UNLOCK(); 185 186 /* Wait for delete to complete */ 187 mutex_enter(&tgt->target_mutex); 188 while ((tgt->target_refcount > 0) || 189 (tgt->target_state != TS_DELETING)) { 190 cv_wait(&tgt->target_cv, &tgt->target_mutex); 191 } 192 mutex_exit(&tgt->target_mutex); 193 194 pppt_tgt_destroy(tgt); 195 stmf_status = STMF_SUCCESS; 196 197 pppt_deregister_tgt_done: 198 pppt_msg_tx_status(msg, stmf_status); 199 stmf_ic_msg_free(msg); 200 } 201 202 static void 203 pppt_msg_session_destroy(stmf_ic_msg_t *msg) 204 { 205 stmf_ic_session_create_destroy_msg_t *sess_destroy; 206 pppt_tgt_t *tgt; 207 pppt_sess_t *ps; 208 209 sess_destroy = msg->icm_msg; 210 211 PPPT_GLOBAL_LOCK(); 212 213 /* 214 * Look for existing session for this ID 215 */ 216 ps = pppt_sess_lookup_locked(sess_destroy->icscd_session_id, 217 sess_destroy->icscd_tgt_devid, sess_destroy->icscd_rport); 218 219 if (ps == NULL) { 220 PPPT_GLOBAL_UNLOCK(); 221 stmf_ic_msg_free(msg); 222 PPPT_INC_STAT(es_sess_destroy_no_session); 223 return; 224 } 225 226 tgt = ps->ps_target; 227 228 mutex_enter(&tgt->target_mutex); 229 mutex_enter(&ps->ps_mutex); 230 231 /* Release the reference from the lookup */ 232 pppt_sess_rele_locked(ps); 233 234 /* Make sure another thread is not already closing the session */ 235 if (!ps->ps_closed) { 236 /* Found matching open session, quiesce... */ 237 pppt_sess_close_locked(ps); 238 } 239 mutex_exit(&ps->ps_mutex); 240 mutex_exit(&tgt->target_mutex); 241 PPPT_GLOBAL_UNLOCK(); 242 243 stmf_ic_msg_free(msg); 244 } 245 246 static void 247 pppt_msg_scsi_cmd(stmf_ic_msg_t *msg) 248 { 249 pppt_sess_t *pppt_sess; 250 pppt_buf_t *pbuf; 251 stmf_ic_scsi_cmd_msg_t *scmd; 252 pppt_task_t *ptask; 253 scsi_task_t *task; 254 pppt_status_t pppt_status; 255 stmf_local_port_t *lport; 256 stmf_scsi_session_t *stmf_sess; 257 stmf_status_t stmf_status; 258 259 /* 260 * Get a task context 261 */ 262 ptask = pppt_task_alloc(); 263 if (ptask == NULL) { 264 /* 265 * We must be very low on memory. Just free the message 266 * and let the command timeout. 267 */ 268 stmf_ic_msg_free(msg); 269 PPPT_INC_STAT(es_scmd_ptask_alloc_fail); 270 return; 271 } 272 273 scmd = msg->icm_msg; 274 275 /* 276 * Session are created implicitly on the first use of an 277 * IT nexus 278 */ 279 pppt_sess = pppt_sess_lookup_create(scmd->icsc_tgt_devid, 280 scmd->icsc_ini_devid, scmd->icsc_rport, 281 scmd->icsc_session_id, &stmf_status); 282 if (pppt_sess == NULL) { 283 pppt_task_free(ptask); 284 pppt_msg_tx_status(msg, stmf_status); 285 stmf_ic_msg_free(msg); 286 PPPT_INC_STAT(es_scmd_sess_create_fail); 287 return; 288 } 289 290 ptask->pt_sess = pppt_sess; 291 ptask->pt_task_id = scmd->icsc_task_msgid; 292 stmf_sess = pppt_sess->ps_stmf_sess; 293 lport = stmf_sess->ss_lport; 294 295 /* 296 * Add task to our internal task set. 297 */ 298 pppt_status = pppt_task_start(ptask); 299 300 if (pppt_status != 0) { 301 /* Release hold from pppt_sess_lookup_create() */ 302 PPPT_LOG(CE_WARN, "Duplicate taskid from remote node 0x%llx", 303 (longlong_t)scmd->icsc_task_msgid); 304 pppt_task_free(ptask); 305 pppt_sess_rele(pppt_sess); 306 pppt_msg_tx_status(msg, STMF_ALREADY); 307 stmf_ic_msg_free(msg); 308 PPPT_INC_STAT(es_scmd_dup_task_count); 309 return; 310 } 311 312 /* 313 * Allocate STMF task context 314 */ 315 ptask->pt_stmf_task = stmf_task_alloc(lport, stmf_sess, 316 scmd->icsc_task_lun_no, 317 scmd->icsc_task_cdb_length, 0); 318 if (ptask->pt_stmf_task == NULL) { 319 (void) pppt_task_done(ptask); 320 pppt_task_free(ptask); 321 pppt_sess_rele(pppt_sess); 322 pppt_msg_tx_status(msg, STMF_ALLOC_FAILURE); 323 stmf_ic_msg_free(msg); 324 PPPT_INC_STAT(es_scmd_stask_alloc_fail); 325 return; 326 } 327 328 task = ptask->pt_stmf_task; 329 task->task_port_private = ptask; 330 task->task_flags = scmd->icsc_task_flags; 331 task->task_additional_flags = 0; 332 task->task_priority = 0; 333 334 /* 335 * Set task->task_mgmt_function to TM_NONE for a normal SCSI task 336 * or one of these values for a task management command: 337 * 338 * TM_ABORT_TASK *** 339 * TM_ABORT_TASK_SET 340 * TM_CLEAR_ACA 341 * TM_CLEAR_TASK_SET 342 * TM_LUN_RESET 343 * TM_TARGET_WARM_RESET 344 * TM_TARGET_COLD_RESET 345 * 346 * *** Note that STMF does not currently support TM_ABORT_TASK so 347 * port providers must implement this command on their own 348 * (e.g. lookup the desired task and call stmf_abort). 349 */ 350 task->task_mgmt_function = scmd->icsc_task_mgmt_function; 351 352 task->task_max_nbufs = 1; /* Don't allow parallel xfers */ 353 task->task_cmd_seq_no = msg->icm_msgid; 354 task->task_expected_xfer_length = 355 scmd->icsc_task_expected_xfer_length; 356 357 if (scmd->icsc_task_cdb_length) { 358 bcopy(scmd->icsc_task_cdb, task->task_cdb, 359 scmd->icsc_task_cdb_length); 360 } 361 bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16); 362 363 if (scmd->icsc_immed_data_len) { 364 pbuf = ptask->pt_immed_data; 365 pbuf->pbuf_immed_msg = msg; 366 pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len; 367 pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len; 368 pbuf->pbuf_stmf_buf->db_relative_offset = 0; 369 pbuf->pbuf_stmf_buf->db_sglist[0].seg_length = 370 scmd->icsc_immed_data_len; 371 pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr = 372 scmd->icsc_immed_data; 373 374 stmf_post_task(task, pbuf->pbuf_stmf_buf); 375 } else { 376 stmf_post_task(task, NULL); 377 stmf_ic_msg_free(msg); 378 } 379 } 380 381 static void 382 pppt_msg_data_xfer_done(stmf_ic_msg_t *msg) 383 { 384 pppt_task_t *pppt_task; 385 stmf_ic_scsi_data_xfer_done_msg_t *data_xfer_done; 386 387 data_xfer_done = msg->icm_msg; 388 389 /* 390 * Find task 391 */ 392 pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid); 393 394 /* If we found one, complete the transfer */ 395 if (pppt_task != NULL) { 396 pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status); 397 } 398 399 stmf_ic_msg_free(msg); 400 } 401 402 static void 403 pppt_msg_handle_status(stmf_ic_msg_t *msg) 404 { 405 /* Don't care for now */ 406 stmf_ic_msg_free(msg); 407 } 408