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