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/types.h>
  27 #include <sys/stat.h>
  28 #include <sys/conf.h>
  29 #include <sys/ddi.h>
  30 #include <sys/sunddi.h>
  31 #include <sys/modctl.h>
  32 #include <sys/socket.h>
  33 #include <netinet/in.h>
  34 
  35 #include <sys/ib/clients/iser/iser.h>
  36 
  37 /*
  38  * iser.c
  39  *    DDI and core routines for Solaris iSER implementation.
  40  */
  41 
  42 iser_state_t    *iser_state = NULL;     /* global state */
  43 ddi_taskq_t     *iser_taskq = NULL;     /* global taskq */
  44 
  45 /* set B_TRUE for console logging */
  46 boolean_t iser_logging = B_FALSE;
  47 
  48 /* Driver functions */
  49 static int iser_attach(dev_info_t *, ddi_attach_cmd_t);
  50 static int iser_detach(dev_info_t *, ddi_detach_cmd_t);
  51 static int iser_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
  52 static int iser_open(dev_t *, int, int, cred_t *);
  53 static int iser_close(dev_t, int, int, cred_t *);
  54 static int iser_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  55 /* static int iser_close(dev_t, int, int, cred_t *); */
  56 
  57 /* Char/Block operations */
  58 static struct cb_ops    iser_cb_ops = {
  59         iser_open,              /* open */
  60         iser_close,             /* close */
  61         nodev,                  /* strategy */
  62         nodev,                  /* print */
  63         nodev,                  /* dump */
  64         nodev,                  /* read */
  65         nodev,                  /* write */
  66         iser_ioctl,             /* ioctl */
  67         nodev,                  /* devmap */
  68         nodev,                  /* mmap */
  69         nodev,                  /* segmap */
  70         nochpoll,               /* poll */
  71         ddi_prop_op,            /* prop_op */
  72         NULL,                   /* stream */
  73         D_MP,                   /* cb_flag */
  74         CB_REV,                 /* rev */
  75         nodev,                  /* int (*cb_aread)() */
  76         nodev,                  /* int (*cb_awrite)() */
  77 };
  78 
  79 /* Device operations */
  80 static struct dev_ops iser_ops = {
  81         DEVO_REV,               /* devo_rev, */
  82         0,                      /* refcnt  */
  83         iser_getinfo,           /* getinfo */
  84         nulldev,                /* identify */
  85         nulldev,                /* probe */
  86         iser_attach,            /* attach */
  87         iser_detach,            /* detach */
  88         nodev,                  /* reset */
  89         &iser_cb_ops,               /* cb_ops */
  90         NULL,                   /* bus ops */
  91         NULL,                   /* power */
  92         ddi_quiesce_not_needed  /* quiesce */
  93 };
  94 
  95 /* Module Driver Info */
  96 #define ISER_NAME_VERSION       "iSCSI Extensions for RDMA"
  97 static struct modldrv iser_modldrv = {
  98         &mod_driverops,
  99         ISER_NAME_VERSION,
 100         &iser_ops,
 101 };
 102 
 103 /* Module Linkage */
 104 static struct modlinkage iser_modlinkage = {
 105         MODREV_1,
 106         &iser_modldrv,
 107         NULL
 108 };
 109 
 110 /*
 111  * _init()
 112  */
 113 int
 114 _init(void)
 115 {
 116         int     status;
 117 
 118         iser_state = kmem_zalloc(sizeof (iser_state_t), KM_SLEEP);
 119         status = mod_install(&iser_modlinkage);
 120         if (status != DDI_SUCCESS) {
 121                 kmem_free(iser_state, sizeof (iser_state_t));
 122         }
 123 
 124         return (status);
 125 }
 126 
 127 /*
 128  * _info()
 129  */
 130 int
 131 _info(struct modinfo *modinfop)
 132 {
 133         return (mod_info(&iser_modlinkage, modinfop));
 134 }
 135 
 136 /*
 137  * _fini()
 138  */
 139 int
 140 _fini(void)
 141 {
 142         int status;
 143 
 144         status = mod_remove(&iser_modlinkage);
 145         if (status != DDI_SUCCESS) {
 146                 return (status);
 147         }
 148         kmem_free(iser_state, sizeof (iser_state_t));
 149 
 150         return (DDI_SUCCESS);
 151 }
 152 
 153 /*
 154  * iser_attach()
 155  */
 156 static int
 157 iser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 158 {
 159         int             instance;
 160         int             status;
 161 
 162         switch (cmd) {
 163         case DDI_ATTACH:
 164                 ISER_LOG(CE_CONT, "iser_attach: DDI_ATTACH");
 165                 instance = ddi_get_instance(dip);
 166 
 167                 iser_state->is_dip = dip;
 168                 iser_state->is_instance = instance;
 169 
 170                 /* Initialize the open refcnt and it's lock */
 171                 iser_state->is_open_refcnt = 0;
 172                 mutex_init(&iser_state->is_refcnt_lock, NULL, MUTEX_DRIVER,
 173                     NULL);
 174 
 175                 iser_taskq = ddi_taskq_create(dip, "iser_taskq",
 176                     ISER_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
 177 
 178                 if (iser_taskq == NULL) {
 179                         ISER_LOG(CE_CONT, "%s%d: failed to create taskq",
 180                             "iser", instance);
 181                         mutex_destroy(&iser_state->is_refcnt_lock);
 182                         return (DDI_FAILURE);
 183                 }
 184 
 185                 /* initialize iSER as IB service */
 186                 status = iser_ib_init();
 187                 if (status != DDI_SUCCESS) {
 188                         ddi_taskq_destroy(iser_taskq);
 189                         mutex_destroy(&iser_state->is_refcnt_lock);
 190                         ISER_LOG(CE_CONT, "%s%d: failed to initialize IB",
 191                             "iser", instance);
 192                         return (DDI_FAILURE);
 193                 }
 194 
 195                 status = ddi_create_minor_node(
 196                     dip, ddi_get_name(dip), S_IFCHR, instance,
 197                     DDI_PSEUDO, 0);
 198                 if (status != DDI_SUCCESS) {
 199                         (void) iser_ib_fini();
 200                         ddi_taskq_destroy(iser_taskq);
 201                         mutex_destroy(&iser_state->is_refcnt_lock);
 202                         ISER_LOG(CE_CONT, "%s%d: failed ddi_create_minor_node",
 203                             "iser", instance);
 204                         return (DDI_FAILURE);
 205                 }
 206 
 207                 ddi_report_dev(dip);
 208 
 209                 return (DDI_SUCCESS);
 210 
 211         case DDI_RESUME:
 212                 ISER_LOG(CE_CONT, "iser_detach: DDI_RESUME unsupported");
 213                 return (DDI_FAILURE);
 214 
 215         default:
 216                 ISER_LOG(CE_CONT, "%s%d: unknown cmd in attach (0x%x)", "iser",
 217                     instance, cmd);
 218                 return (DDI_FAILURE);
 219         }
 220 }
 221 
 222 /*
 223  * iser_detach()
 224  */
 225 static int
 226 iser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 227 {
 228         mutex_enter(&iser_state->is_refcnt_lock);
 229         if (iser_state->is_open_refcnt > 0) {
 230                 mutex_exit(&iser_state->is_refcnt_lock);
 231                 return (DDI_FAILURE);
 232         }
 233         mutex_exit(&iser_state->is_refcnt_lock);
 234         mutex_destroy(&iser_state->is_refcnt_lock);
 235 
 236         switch (cmd) {
 237         case DDI_DETACH:
 238                 ISER_LOG(CE_CONT, "iser_detach: DDI_DETACH");
 239 
 240                 if (iser_ib_fini() != DDI_SUCCESS) {
 241                         ISER_LOG(CE_CONT, "iser_ib_fini failed");
 242                         return (DDI_FAILURE);
 243                 }
 244 
 245                 if (iser_taskq != NULL) {
 246                         ddi_taskq_destroy(iser_taskq);
 247                         iser_taskq = NULL;
 248                 }
 249                 ddi_remove_minor_node(dip, NULL);
 250 
 251                 return (DDI_SUCCESS);
 252 
 253         case DDI_SUSPEND:
 254                 ISER_LOG(CE_CONT, "iser_detach: DDI_SUSPEND unsupported");
 255                 return (DDI_FAILURE);
 256 
 257         default:
 258                 ISER_LOG(CE_CONT, "iser: unknown cmd in detach (0x%x)", cmd);
 259                 return (DDI_FAILURE);
 260         }
 261 }
 262 
 263 /*
 264  * iser_getinfo()
 265  */
 266 /* ARGSUSED */
 267 static int
 268 iser_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 269 {
 270         switch (cmd) {
 271         case DDI_INFO_DEVT2DEVINFO:
 272                 *result = (void *)iser_state->is_dip;
 273                 return (DDI_SUCCESS);
 274 
 275         case DDI_INFO_DEVT2INSTANCE:
 276                 *result = NULL;
 277                 return (DDI_SUCCESS);
 278 
 279         default:
 280                 return (DDI_FAILURE);
 281         }
 282 
 283 }
 284 
 285 /*
 286  * iser_open()
 287  */
 288 /* ARGSUSED */
 289 static int
 290 iser_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 291 {
 292         minor_t         instance;
 293         int             status;
 294 
 295         instance = getminor(*devp);
 296 
 297         /* Register the transport with IDM */
 298         status = iser_idm_register();
 299         if (status != DDI_SUCCESS) {
 300                 ISER_LOG(CE_CONT, "%s%d: failed to register with IDM",
 301                     "iser", instance);
 302                 return (ENXIO);
 303         }
 304 
 305         /* Increment our open refcnt */
 306         mutex_enter(&iser_state->is_refcnt_lock);
 307         iser_state->is_open_refcnt++;
 308         mutex_exit(&iser_state->is_refcnt_lock);
 309 
 310         return (DDI_SUCCESS);
 311 }
 312 
 313 /*
 314  * iser_close()
 315  */
 316 /* ARGSUSED */
 317 static int
 318 iser_close(dev_t devp, int flag, int otyp, cred_t *credp)
 319 {
 320         ASSERT(iser_state->is_open_refcnt != 0);
 321 
 322         mutex_enter(&iser_state->is_refcnt_lock);
 323         iser_state->is_open_refcnt--;
 324         mutex_exit(&iser_state->is_refcnt_lock);
 325 
 326         return (DDI_SUCCESS);
 327 }
 328 
 329 iser_status_t
 330 iser_register_service(idm_svc_t *idm_svc)
 331 {
 332 
 333         return (iser_ib_register_service(idm_svc));
 334 }
 335 
 336 iser_status_t
 337 iser_bind_service(idm_svc_t *idm_svc)
 338 {
 339 
 340         return (iser_ib_bind_service(idm_svc));
 341 }
 342 
 343 void
 344 iser_unbind_service(idm_svc_t *idm_svc)
 345 {
 346 
 347         iser_ib_unbind_service(idm_svc);
 348 }
 349 
 350 void
 351 iser_deregister_service(idm_svc_t *idm_svc)
 352 {
 353 
 354         iser_ib_deregister_service(idm_svc);
 355 }
 356 
 357 /*
 358  * iser_path_exists
 359  * This function takes in a pair of endpoints and determines if an iSER path
 360  * exists between the two. The actual path information (required for creating
 361  * a RC channel) is not returned, instead a boolean value indicating if a path
 362  * exists is returned.
 363  *
 364  * To use an implicit source, a value of NULL is allowed for laddr.
 365  */
 366 boolean_t
 367 iser_path_exists(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
 368 {
 369 
 370         ibt_ip_addr_t           remote_ip, local_ip;
 371         ibt_path_info_t         path;
 372         int                     status;
 373 
 374         iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
 375         iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
 376 
 377         status = iser_ib_get_paths(&local_ip, &remote_ip, &path, NULL);
 378 
 379         return ((status == IBT_SUCCESS) ? B_TRUE : B_FALSE);
 380 }
 381 
 382 /*
 383  * iser_channel_alloc
 384  * This function allocates a reliable communication channel between the
 385  * given endpoints.
 386  */
 387 iser_chan_t *
 388 iser_channel_alloc(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
 389 {
 390         ibt_ip_addr_t           remote_ip, local_ip;
 391 
 392         iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
 393         iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
 394 
 395         return (iser_ib_alloc_channel_pathlookup(&local_ip, &remote_ip));
 396 }
 397 
 398 /*
 399  * iser_channel_open
 400  * This function opens the already allocated communication channel between the
 401  * two endpoints.
 402  */
 403 iser_status_t
 404 iser_channel_open(iser_chan_t *chan)
 405 {
 406         return (iser_ib_open_rc_channel(chan));
 407 }
 408 
 409 /*
 410  * iser_channel_close
 411  * This function closes the already opened communication channel between the
 412  * two endpoints.
 413  */
 414 void
 415 iser_channel_close(iser_chan_t *chan)
 416 {
 417         iser_ib_close_rc_channel(chan);
 418 }
 419 
 420 /*
 421  * iser_channel_free
 422  * This function frees the channel between the given endpoints
 423  */
 424 void
 425 iser_channel_free(iser_chan_t *chan)
 426 {
 427         iser_ib_free_rc_channel(chan);
 428 }
 429 
 430 /* ARGSUSED */
 431 static int
 432 iser_ioctl(dev_t devp, int cmd, intptr_t arg, int mode, cred_t *credp,
 433     int *rvalp)
 434 {
 435         return (DDI_SUCCESS);
 436 }