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