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  */
  25 
  26 /*
  27  * IB CM handlers for s Solaris SCSI RDMA Protocol Target (SRP)
  28  * transport port provider module for the COMSTAR framework.
  29  */
  30 
  31 #include <sys/cpuvar.h>
  32 #include <sys/types.h>
  33 #include <sys/conf.h>
  34 #include <sys/stat.h>
  35 #include <sys/file.h>
  36 #include <sys/ddi.h>
  37 #include <sys/sunddi.h>
  38 #include <sys/modctl.h>
  39 #include <sys/sysmacros.h>
  40 #include <sys/sdt.h>
  41 #include <sys/taskq.h>
  42 #include <sys/ib/ibtl/ibti.h>
  43 
  44 #include <sys/stmf.h>
  45 #include <sys/stmf_ioctl.h>
  46 #include <sys/portif.h>
  47 
  48 #include "srp.h"
  49 #include "srpt_impl.h"
  50 #include "srpt_cm.h"
  51 #include "srpt_stp.h"
  52 #include "srpt_ch.h"
  53 
  54 extern uint16_t srpt_send_msg_depth;
  55 extern srpt_ctxt_t  *srpt_ctxt;
  56 
  57 /*
  58  * srpt_cm_req_hdlr() - Login request
  59  *
  60  * CM has called back with a CM REQ message associated with an
  61  * SRP initiator login request.
  62  */
  63 static ibt_cm_status_t
  64 srpt_cm_req_hdlr(srpt_target_port_t *tgt, ibt_cm_event_t *event,
  65         ibt_cm_return_args_t *ret_args, void *ret_priv_data,
  66         ibt_priv_data_len_t ret_priv_data_len)
  67 {
  68         ibt_cm_status_t         status;
  69         ibt_cm_req_rcv_t        *req;
  70         srp_login_req_t         login;
  71         srp_login_rej_t         login_rej;
  72         srp_login_rsp_t         login_rsp;
  73         srpt_channel_t          *ch = NULL;
  74         char                    remote_gid[SRPT_ALIAS_LEN];
  75         char                    local_gid[SRPT_ALIAS_LEN];
  76 
  77         ASSERT(tgt != NULL);
  78         req = &event->cm_event.req;
  79 
  80         if (event->cm_priv_data_len <  sizeof (srp_login_req_t)) {
  81                 SRPT_DPRINTF_L2("cm_req_hdlr, IU size expected (>= %d),"
  82                     " received size (%d)", (uint_t)sizeof (srp_login_req_t),
  83                     event->cm_priv_data_len);
  84                 return (IBT_CM_REJECT);
  85         }
  86 
  87         if (event->cm_priv_data == NULL) {
  88                 SRPT_DPRINTF_L2("cm_req_hdlr, NULL ULP private data pointer");
  89                 return (IBT_CM_REJECT);
  90         }
  91 
  92         if (ret_priv_data_len <  sizeof (srp_login_rej_t)) {
  93                 SRPT_DPRINTF_L2("cm_req_hdlr, return private len too"
  94                     " small (%d)", ret_priv_data_len);
  95                 return (IBT_CM_REJECT);
  96         }
  97 
  98         if (ret_priv_data == NULL) {
  99                 SRPT_DPRINTF_L2("cm_req_hdlr, NULL ULP return private data"
 100                     " pointer");
 101                 return (IBT_CM_REJECT);
 102         }
 103 
 104         /*
 105          * Copy to avoid potential alignment problems, process login
 106          * creating a new channel and possibly session.
 107          */
 108         bcopy(event->cm_priv_data, &login,  sizeof (login));
 109 
 110         ALIAS_STR(local_gid,
 111             req->req_prim_addr.av_sgid.gid_prefix,
 112             req->req_prim_addr.av_sgid.gid_guid);
 113         ALIAS_STR(remote_gid,
 114             req->req_prim_addr.av_dgid.gid_prefix,
 115             req->req_prim_addr.av_dgid.gid_guid);
 116 
 117         ch = srpt_stp_login(tgt, &login, &login_rsp,
 118             &login_rej, req->req_prim_hca_port, local_gid, remote_gid);
 119         if (ch != NULL) {
 120                 bcopy(&login_rsp, ret_priv_data,  SRP_LOGIN_RSP_SIZE);
 121                 ret_args->cm_ret_len = SRP_LOGIN_RSP_SIZE;
 122 
 123                 SRPT_DPRINTF_L3("cm_req_hdlr, rsp priv len(%d)"
 124                     " ch created on port(%d)"
 125                     ", cm_req_hdlr, req ra_out(%d), ra_in(%d)"
 126                     ", retry(%d)",
 127                     ret_args->cm_ret_len, req->req_prim_hca_port,
 128                     req->req_rdma_ra_out, req->req_rdma_ra_in,
 129                     req->req_retry_cnt);
 130 
 131                 ret_args->cm_ret.rep.cm_channel = ch->ch_chan_hdl;
 132                 ret_args->cm_ret.rep.cm_rdma_ra_out =
 133                     min(tgt->tp_ioc->ioc_attr.hca_max_rdma_out_chan,
 134                     req->req_rdma_ra_in);
 135                 ret_args->cm_ret.rep.cm_rdma_ra_in =
 136                     min(tgt->tp_ioc->ioc_attr.hca_max_rdma_in_chan,
 137                     req->req_rdma_ra_out);
 138                 ret_args->cm_ret.rep.cm_rnr_retry_cnt = req->req_retry_cnt;
 139 
 140                 SRPT_DPRINTF_L3("cm_req_hdlr, hca_max_rdma_in_chan (%d)"
 141                     ", hca_max_rdma_out_chan (%d)"
 142                     ", updated ra_out(%d), ra_in(%d), retry(%d)",
 143                     tgt->tp_ioc->ioc_attr.hca_max_rdma_in_chan,
 144                     tgt->tp_ioc->ioc_attr.hca_max_rdma_out_chan,
 145                     ret_args->cm_ret.rep.cm_rdma_ra_out,
 146                     ret_args->cm_ret.rep.cm_rdma_ra_in,
 147                     ret_args->cm_ret.rep.cm_rnr_retry_cnt);
 148                 status = IBT_CM_ACCEPT;
 149 
 150         } else {
 151                 bcopy(&login_rej, ret_priv_data,  sizeof (login_rej));
 152                 ret_args->cm_ret_len =  sizeof (login_rej);
 153                 status = IBT_CM_REJECT;
 154         }
 155 
 156         return (status);
 157 }
 158 
 159 /*
 160  * srpt_cm_conn_est_hdlr() - Connection established
 161  *
 162  * CM has called back to inform us that a connection attempt has
 163  * completed (explicit or implicit) and may now be used.
 164  */
 165 /* ARGSUSED */
 166 static ibt_cm_status_t
 167 srpt_cm_conn_est_hdlr(srpt_target_port_t *tgt, ibt_cm_event_t *event)
 168 {
 169         srpt_channel_t          *ch;
 170 
 171         ASSERT(tgt != NULL);
 172         ASSERT(event != NULL);
 173 
 174         ch = (srpt_channel_t *)ibt_get_chan_private(event->cm_channel);
 175         ASSERT(ch != NULL);
 176 
 177         SRPT_DPRINTF_L3("cm_conn_est_hdlr, invoked for ch(%p)",
 178             (void *)ch);
 179 
 180         rw_enter(&ch->ch_rwlock, RW_WRITER);
 181         if (ch->ch_state != SRPT_CHANNEL_CONNECTING &&
 182             ch->ch_state != SRPT_CHANNEL_CONNECTED) {
 183                 SRPT_DPRINTF_L2("cm_conn_est_hdlr, invalid ch state (%d)",
 184                     ch->ch_state);
 185                 rw_exit(&ch->ch_rwlock);
 186                 return (IBT_CM_REJECT);
 187         }
 188 
 189         ch->ch_state = SRPT_CHANNEL_CONNECTED;
 190 
 191         rw_exit(&ch->ch_rwlock);
 192         return (IBT_CM_ACCEPT);
 193 }
 194 
 195 /*
 196  * srpt_cm_conn_closed_hdlr() - Channel closed
 197  *
 198  * CM callback indicating a channel has been completely closed.
 199  */
 200 /* ARGSUSED */
 201 static ibt_cm_status_t
 202 srpt_cm_conn_closed_hdlr(srpt_target_port_t *tgt, ibt_cm_event_t *event)
 203 {
 204         ibt_cm_status_t         status = IBT_CM_ACCEPT;
 205         srpt_channel_t          *ch;
 206 
 207         ASSERT(tgt != NULL);
 208         ASSERT(event != NULL);
 209 
 210         ch = (srpt_channel_t *)ibt_get_chan_private(event->cm_channel);
 211         ASSERT(ch != NULL);
 212 
 213         SRPT_DPRINTF_L3("cm_conn_closed_hdlr, invoked for chan_hdl(%p),"
 214             " event(%d)", (void *)ch->ch_chan_hdl,
 215             event->cm_event.closed);
 216 
 217         switch (event->cm_event.closed) {
 218 
 219         case IBT_CM_CLOSED_DREP_RCVD:
 220         case IBT_CM_CLOSED_DREQ_TIMEOUT:
 221         case IBT_CM_CLOSED_DUP:
 222         case IBT_CM_CLOSED_ABORT:
 223         case IBT_CM_CLOSED_ALREADY:
 224                 /*
 225                  * These cases indicate the SRP target initiated
 226                  * the closing of the channel and it is now closed.
 227                  * Cleanup the channel (which will remove the targets
 228                  * reference) and then release CM's reference.
 229                  */
 230                 SRPT_DPRINTF_L3("cm_conn_closed_hdlr, local close call-back");
 231                 srpt_ch_cleanup(ch);
 232                 srpt_ch_release_ref(ch, 1);
 233                 break;
 234 
 235         case IBT_CM_CLOSED_DREQ_RCVD:
 236         case IBT_CM_CLOSED_REJ_RCVD:
 237         case IBT_CM_CLOSED_STALE:
 238                 /*
 239                  * These cases indicate that the SRP initiator is closing
 240                  * the channel.  CM will have already closed the RC channel,
 241                  * so simply initiate cleanup which will remove the target
 242                  * ports reference to the channel and then release the
 243                  * reference held by the CM.
 244                  */
 245                 SRPT_DPRINTF_L3("cm_conn_closed_hdlr, remote close,"
 246                     " free channel");
 247                 if (ch != NULL) {
 248                         srpt_ch_cleanup(ch);
 249                         srpt_ch_release_ref(ch, 1);
 250                 } else {
 251                         SRPT_DPRINTF_L2("cm_conn_closed_hdlr, NULL channel");
 252                 }
 253                 break;
 254 
 255         default:
 256                 SRPT_DPRINTF_L2("cm_conn_closed_hdlr, unknown close type (%d)",
 257                     event->cm_event.closed);
 258                 status = IBT_CM_DEFAULT;
 259                 break;
 260         }
 261         return (status);
 262 }
 263 
 264 /*
 265  * srpt_cm_failure_hdlr() - Called when the channel is in error.  Cleanup
 266  * and release the channel.
 267  */
 268 static ibt_cm_status_t
 269 srpt_cm_failure_hdlr(ibt_cm_event_t *event)
 270 {
 271         srpt_channel_t          *ch;
 272 
 273         ASSERT(event != NULL);
 274 
 275         ch = (srpt_channel_t *)ibt_get_chan_private(event->cm_channel);
 276         ASSERT(ch != NULL);
 277 
 278         SRPT_DPRINTF_L3("cm_failure_hdlr, chan_hdl: 0x%p, code: %d"
 279             "msg: %d reason: %d", (void *)event->cm_channel,
 280             event->cm_event.failed.cf_code,
 281             event->cm_event.failed.cf_msg,
 282             event->cm_event.failed.cf_reason);
 283 
 284         srpt_ch_cleanup(ch);
 285         srpt_ch_release_ref(ch, 1);
 286 
 287         return (IBT_CM_ACCEPT);
 288 }
 289 
 290 /*
 291  * srpt_cm_hdlr() - CM call-back handler.
 292  */
 293 ibt_cm_status_t
 294 srpt_cm_hdlr(void *cm_private, ibt_cm_event_t *event,
 295         ibt_cm_return_args_t *ret_args, void *ret_priv_data,
 296         ibt_priv_data_len_t ret_len_max)
 297 {
 298         ibt_cm_status_t         status = IBT_CM_ACCEPT;
 299 
 300         switch (event->cm_type) {
 301 
 302         case IBT_CM_EVENT_REQ_RCV:
 303                 SRPT_DPRINTF_L3("cm_hdlr, REQ received");
 304                 status = srpt_cm_req_hdlr((srpt_target_port_t *)cm_private,
 305                     event, ret_args, ret_priv_data, ret_len_max);
 306                 break;
 307 
 308         case IBT_CM_EVENT_REP_RCV:
 309                 SRPT_DPRINTF_L3("cm_hdlr, REP received");
 310                 break;
 311 
 312         case IBT_CM_EVENT_MRA_RCV:
 313                 SRPT_DPRINTF_L3("cm_hdlr, MRA received");
 314                 break;
 315 
 316         case IBT_CM_EVENT_CONN_EST:
 317                 SRPT_DPRINTF_L3("cm_hdlr, Connection established");
 318                 status = srpt_cm_conn_est_hdlr(
 319                     (srpt_target_port_t *)cm_private, event);
 320                 break;
 321 
 322         case IBT_CM_EVENT_CONN_CLOSED:
 323                 SRPT_DPRINTF_L3("cm_hdlr, Connection closed");
 324                 status = srpt_cm_conn_closed_hdlr(
 325                     (srpt_target_port_t *)cm_private, event);
 326                 break;
 327 
 328         case IBT_CM_EVENT_FAILURE:
 329                 SRPT_DPRINTF_L3("cm_hdlr, Event failure");
 330                 status = srpt_cm_failure_hdlr(event);
 331                 break;
 332 
 333         case IBT_CM_EVENT_LAP_RCV:
 334                 SRPT_DPRINTF_L3("cm_hdlr, LAP received");
 335                 break;
 336 
 337         case IBT_CM_EVENT_APR_RCV:
 338                 SRPT_DPRINTF_L3("cm_hdlr, APR received");
 339                 break;
 340 
 341         default:
 342                 SRPT_DPRINTF_L3("cm_hdlr, unknown event received");
 343                 break;
 344         }
 345 
 346         return (status);
 347 }