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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/cpuvar.h> 27 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/file.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/modctl.h> 33 #include <sys/sysmacros.h> 34 35 #include <sys/socket.h> 36 #include <sys/strsubr.h> 37 #include <sys/door.h> 38 39 #include <sys/stmf.h> 40 #include <sys/stmf_ioctl.h> 41 #include <sys/portif.h> 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 121 PPPT_GLOBAL_LOCK(); 122 if (pppt_global.global_svc_state != PSS_ENABLED) { 123 stmf_status = STMF_FAILURE; 124 PPPT_INC_STAT(es_tgt_reg_svc_disabled); 125 goto pppt_register_tgt_done; 126 } 127 128 /* 129 * For now we assume that the marshall/unmarshall code is responsible 130 * for validating the message length and ensuring the resulting 131 * request structure is self consistent. Make sure this 132 * target doesn't already exist. 133 */ 134 if ((result = pppt_tgt_lookup_locked(reg_port->icrp_port_id)) != NULL) { 135 stmf_status = STMF_ALREADY; 136 PPPT_INC_STAT(es_tgt_reg_duplicate); 137 goto pppt_register_tgt_done; 138 } 139 140 result = pppt_tgt_create(reg_port, &stmf_status); 141 142 if (result == NULL) { 143 stmf_status = STMF_TARGET_FAILURE; 144 PPPT_INC_STAT(es_tgt_reg_create_fail); 145 goto pppt_register_tgt_done; 146 } 147 148 avl_add(&pppt_global.global_target_list, result); 149 150 stmf_status = STMF_SUCCESS; 151 152 pppt_register_tgt_done: 153 PPPT_GLOBAL_UNLOCK(); 154 pppt_msg_tx_status(msg, stmf_status); 155 stmf_ic_msg_free(msg); 156 } 157 158 static void 159 pppt_msg_tgt_deregister(stmf_ic_msg_t *msg) 160 { 161 stmf_ic_dereg_port_msg_t *dereg_port; 162 stmf_status_t stmf_status; 163 pppt_tgt_t *tgt; 164 165 PPPT_GLOBAL_LOCK(); 166 if (pppt_global.global_svc_state != PSS_ENABLED) { 167 PPPT_GLOBAL_UNLOCK(); 168 stmf_status = STMF_FAILURE; 169 PPPT_INC_STAT(es_tgt_dereg_svc_disabled); 170 goto pppt_deregister_tgt_done; 171 } 172 173 dereg_port = msg->icm_msg; 174 175 /* Lookup target */ 176 if ((tgt = pppt_tgt_lookup_locked(dereg_port->icdp_port_id)) == NULL) { 177 PPPT_GLOBAL_UNLOCK(); 178 stmf_status = STMF_NOT_FOUND; 179 PPPT_INC_STAT(es_tgt_dereg_not_found); 180 goto pppt_deregister_tgt_done; 181 } 182 avl_remove(&pppt_global.global_target_list, tgt); 183 pppt_tgt_async_delete(tgt); 184 185 PPPT_GLOBAL_UNLOCK(); 186 187 /* Wait for delete to complete */ 188 mutex_enter(&tgt->target_mutex); 189 while ((tgt->target_refcount > 0) || 190 (tgt->target_state != TS_DELETING)) { 191 cv_wait(&tgt->target_cv, &tgt->target_mutex); 192 } 193 mutex_exit(&tgt->target_mutex); 194 195 pppt_tgt_destroy(tgt); 196 stmf_status = STMF_SUCCESS; 197 198 pppt_deregister_tgt_done: 199 pppt_msg_tx_status(msg, stmf_status); 200 stmf_ic_msg_free(msg); 201 } 202 203 static void 204 pppt_msg_session_destroy(stmf_ic_msg_t *msg) 205 { 206 stmf_ic_session_create_destroy_msg_t *sess_destroy; 207 pppt_tgt_t *tgt; 208 pppt_sess_t *ps; 209 210 sess_destroy = msg->icm_msg; 211 212 PPPT_GLOBAL_LOCK(); 213 214 /* 215 * Look for existing session for this ID 216 */ 217 ps = pppt_sess_lookup_locked(sess_destroy->icscd_session_id, 218 sess_destroy->icscd_tgt_devid, sess_destroy->icscd_ini_devid); 219 220 if (ps == NULL) { 221 PPPT_GLOBAL_UNLOCK(); 222 stmf_ic_msg_free(msg); 223 PPPT_INC_STAT(es_sess_destroy_no_session); 224 return; 225 } 226 227 tgt = ps->ps_target; 228 229 mutex_enter(&tgt->target_mutex); 230 mutex_enter(&ps->ps_mutex); 231 232 /* Release the reference from the lookup */ 233 pppt_sess_rele_locked(ps); 234 235 /* Make sure another thread is not already closing the session */ 236 if (!ps->ps_closed) { 237 /* Found matching open session, quiesce... */ 238 pppt_sess_close_locked(ps); 239 } 240 mutex_exit(&ps->ps_mutex); 241 mutex_exit(&tgt->target_mutex); 242 PPPT_GLOBAL_UNLOCK(); 243 244 stmf_ic_msg_free(msg); 245 } 246 247 static void 248 pppt_msg_scsi_cmd(stmf_ic_msg_t *msg) 249 { 250 pppt_sess_t *pppt_sess; 251 pppt_buf_t *pbuf; 252 stmf_ic_scsi_cmd_msg_t *scmd; 253 pppt_task_t *ptask; 254 scsi_task_t *task; 255 pppt_status_t pppt_status; 256 stmf_local_port_t *lport; 257 stmf_scsi_session_t *stmf_sess; 258 stmf_status_t stmf_status; 259 260 /* 261 * Get a task context 262 */ 263 ptask = pppt_task_alloc(); 264 if (ptask == NULL) { 265 /* 266 * We must be very low on memory. Just free the message 267 * and let the command timeout. 268 */ 269 stmf_ic_msg_free(msg); 270 PPPT_INC_STAT(es_scmd_ptask_alloc_fail); 271 return; 272 } 273 274 scmd = msg->icm_msg; 275 276 /* 277 * Session are created implicitly on the first use of an 278 * IT nexus 279 */ 280 pppt_sess = pppt_sess_lookup_create(scmd->icsc_tgt_devid, 281 scmd->icsc_ini_devid, 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 = STMF_BUFS_MAX; /* Or protocol value */ 353 task->task_cmd_seq_no = msg->icm_msgid; 354 task->task_expected_xfer_length = 355 scmd->icsc_task_expected_xfer_length; 356 357 bcopy(scmd->icsc_task_cdb, task->task_cdb, 358 scmd->icsc_task_cdb_length); 359 bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16); 360 361 if (scmd->icsc_immed_data_len) { 362 pbuf = ptask->pt_immed_data; 363 pbuf->pbuf_immed_msg = msg; 364 pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len; 365 pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len; 366 pbuf->pbuf_stmf_buf->db_relative_offset = 0; 367 pbuf->pbuf_stmf_buf->db_sglist[0].seg_length = 368 scmd->icsc_immed_data_len; 369 pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr = 370 scmd->icsc_immed_data; 371 372 stmf_post_task(task, pbuf->pbuf_stmf_buf); 373 } else { 374 stmf_post_task(task, NULL); 375 stmf_ic_msg_free(msg); 376 } 377 } 378 379 static void 380 pppt_msg_data_xfer_done(stmf_ic_msg_t *msg) 381 { 382 pppt_task_t *pppt_task; 383 stmf_ic_scsi_data_xfer_done_msg_t *data_xfer_done; 384 385 data_xfer_done = msg->icm_msg; 386 387 /* 388 * Find task 389 */ 390 pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid); 391 392 /* If we found one, complete the transfer */ 393 if (pppt_task != NULL) { 394 pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status); 395 } 396 397 stmf_ic_msg_free(msg); 398 } 399 400 static void 401 pppt_msg_handle_status(stmf_ic_msg_t *msg) 402 { 403 /* Don't care for now */ 404 stmf_ic_msg_free(msg); 405 } 406