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 #include <pppt.h> 42 43 static void pppt_msg_tgt_register(stmf_ic_msg_t *reg_port); 44 45 static void pppt_msg_tgt_deregister(stmf_ic_msg_t *msg); 46 47 static void pppt_msg_session_destroy(stmf_ic_msg_t *msg); 48 49 static void pppt_msg_scsi_cmd(stmf_ic_msg_t *msg); 50 51 static void pppt_msg_data_xfer_done(stmf_ic_msg_t *msg); 52 53 static void pppt_msg_handle_status(stmf_ic_msg_t *msg); 54 55 void 56 pppt_msg_rx(stmf_ic_msg_t *msg) 57 { 58 switch (msg->icm_msg_type) { 59 case STMF_ICM_REGISTER_PROXY_PORT: 60 pppt_msg_tgt_register(msg); 61 break; 62 case STMF_ICM_DEREGISTER_PROXY_PORT: 63 pppt_msg_tgt_deregister(msg); 64 break; 65 case STMF_ICM_SESSION_CREATE: 66 pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED); 67 stmf_ic_msg_free(msg); 68 break; 69 case STMF_ICM_SESSION_DESTROY: 70 pppt_msg_session_destroy(msg); 71 break; 72 case STMF_ICM_SCSI_CMD: 73 pppt_msg_scsi_cmd(msg); 74 break; 75 case STMF_ICM_SCSI_DATA_XFER_DONE: 76 pppt_msg_data_xfer_done(msg); 77 break; 78 case STMF_ICM_SCSI_DATA: 79 /* Ignore, all proxy data will be immediate for now */ 80 pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED); 81 stmf_ic_msg_free(msg); 82 break; 83 case STMF_ICM_STATUS: 84 pppt_msg_handle_status(msg); 85 break; 86 default: 87 /* Other message types are not allowed */ 88 ASSERT(0); 89 break; 90 } 91 } 92 93 void 94 pppt_msg_tx_status(stmf_ic_msg_t *orig_msg, stmf_status_t status) 95 { 96 stmf_ic_msg_t *msg; 97 98 /* 99 * If TX of status fails it should be treated the same as a loss of 100 * connection. We expect the remote node to handle it. 101 */ 102 msg = stmf_ic_status_msg_alloc(status, orig_msg->icm_msg_type, 103 orig_msg->icm_msgid); 104 105 if (msg != NULL) { 106 (void) stmf_ic_tx_msg(msg); 107 } 108 } 109 110 static void 111 pppt_msg_tgt_register(stmf_ic_msg_t *msg) 112 { 113 stmf_ic_reg_port_msg_t *reg_port; 114 pppt_tgt_t *result; 115 stmf_status_t stmf_status; 116 117 reg_port = msg->icm_msg; 118 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_ini_devid); 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_session_id, &stmf_status); 281 if (pppt_sess == NULL) { 282 pppt_task_free(ptask); 283 pppt_msg_tx_status(msg, stmf_status); 284 stmf_ic_msg_free(msg); 285 PPPT_INC_STAT(es_scmd_sess_create_fail); 286 return; 287 } 288 289 ptask->pt_sess = pppt_sess; 290 ptask->pt_task_id = scmd->icsc_task_msgid; 291 stmf_sess = pppt_sess->ps_stmf_sess; 292 lport = stmf_sess->ss_lport; 293 294 /* 295 * Add task to our internal task set. 296 */ 297 pppt_status = pppt_task_start(ptask); 298 299 if (pppt_status != 0) { 300 /* Release hold from pppt_sess_lookup_create() */ 301 PPPT_LOG(CE_WARN, "Duplicate taskid from remote node 0x%llx", 302 (longlong_t)scmd->icsc_task_msgid); 303 pppt_task_free(ptask); 304 pppt_sess_rele(pppt_sess); 305 pppt_msg_tx_status(msg, STMF_ALREADY); 306 stmf_ic_msg_free(msg); 307 PPPT_INC_STAT(es_scmd_dup_task_count); 308 return; 309 } 310 311 /* 312 * Allocate STMF task context 313 */ 314 ptask->pt_stmf_task = stmf_task_alloc(lport, stmf_sess, 315 scmd->icsc_task_lun_no, 316 scmd->icsc_task_cdb_length, 0); 317 if (ptask->pt_stmf_task == NULL) { 318 (void) pppt_task_done(ptask); 319 pppt_task_free(ptask); 320 pppt_sess_rele(pppt_sess); 321 pppt_msg_tx_status(msg, STMF_ALLOC_FAILURE); 322 stmf_ic_msg_free(msg); 323 PPPT_INC_STAT(es_scmd_stask_alloc_fail); 324 return; 325 } 326 327 task = ptask->pt_stmf_task; 328 task->task_port_private = ptask; 329 task->task_flags = scmd->icsc_task_flags; 330 task->task_additional_flags = 0; 331 task->task_priority = 0; 332 333 /* 334 * Set task->task_mgmt_function to TM_NONE for a normal SCSI task 335 * or one of these values for a task management command: 336 * 337 * TM_ABORT_TASK *** 338 * TM_ABORT_TASK_SET 339 * TM_CLEAR_ACA 340 * TM_CLEAR_TASK_SET 341 * TM_LUN_RESET 342 * TM_TARGET_WARM_RESET 343 * TM_TARGET_COLD_RESET 344 * 345 * *** Note that STMF does not currently support TM_ABORT_TASK so 346 * port providers must implement this command on their own 347 * (e.g. lookup the desired task and call stmf_abort). 348 */ 349 task->task_mgmt_function = scmd->icsc_task_mgmt_function; 350 351 task->task_max_nbufs = 1; /* Don't allow parallel xfers */ 352 task->task_cmd_seq_no = msg->icm_msgid; 353 task->task_expected_xfer_length = 354 scmd->icsc_task_expected_xfer_length; 355 356 bcopy(scmd->icsc_task_cdb, task->task_cdb, 357 scmd->icsc_task_cdb_length); 358 bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16); 359 360 if (scmd->icsc_immed_data_len) { 361 pbuf = ptask->pt_immed_data; 362 pbuf->pbuf_immed_msg = msg; 363 pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len; 364 pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len; 365 pbuf->pbuf_stmf_buf->db_relative_offset = 0; 366 pbuf->pbuf_stmf_buf->db_sglist[0].seg_length = 367 scmd->icsc_immed_data_len; 368 pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr = 369 scmd->icsc_immed_data; 370 371 stmf_post_task(task, pbuf->pbuf_stmf_buf); 372 } else { 373 stmf_post_task(task, NULL); 374 stmf_ic_msg_free(msg); 375 } 376 } 377 378 static void 379 pppt_msg_data_xfer_done(stmf_ic_msg_t *msg) 380 { 381 pppt_task_t *pppt_task; 382 stmf_ic_scsi_data_xfer_done_msg_t *data_xfer_done; 383 384 data_xfer_done = msg->icm_msg; 385 386 /* 387 * Find task 388 */ 389 pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid); 390 391 /* If we found one, complete the transfer */ 392 if (pppt_task != NULL) { 393 pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status); 394 } 395 396 stmf_ic_msg_free(msg); 397 } 398 399 static void 400 pppt_msg_handle_status(stmf_ic_msg_t *msg) 401 { 402 /* Don't care for now */ 403 stmf_ic_msg_free(msg); 404 } 405