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 = 0;
 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 }