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, NULL }
 107 };
 108 
 109 /*
 110  * _init()
 111  */
 112 int
 113 _init(void)
 114 {
 115         int     status;
 116 
 117         iser_state = kmem_zalloc(sizeof (iser_state_t), KM_SLEEP);
 118         status = mod_install(&iser_modlinkage);
 119         if (status != DDI_SUCCESS) {
 120                 kmem_free(iser_state, sizeof (iser_state_t));
 121         }
 122 
 123         return (status);
 124 }
 125 
 126 /*
 127  * _info()
 128  */
 129 int
 130 _info(struct modinfo *modinfop)
 131 {
 132         return (mod_info(&iser_modlinkage, modinfop));
 133 }
 134 
 135 /*
 136  * _fini()
 137  */
 138 int
 139 _fini(void)
 140 {
 141         int status;
 142 
 143         status = mod_remove(&iser_modlinkage);
 144         if (status != DDI_SUCCESS) {
 145                 return (status);
 146         }
 147         kmem_free(iser_state, sizeof (iser_state_t));
 148 
 149         return (DDI_SUCCESS);
 150 }
 151 
 152 /*
 153  * iser_attach()
 154  */
 155 static int
 156 iser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 157 {
 158         int             instance;
 159         int             status;
 160 
 161         switch (cmd) {
 162         case DDI_ATTACH:
 163                 ISER_LOG(CE_CONT, "iser_attach: DDI_ATTACH");
 164                 instance = ddi_get_instance(dip);
 165 
 166                 iser_state->is_dip = dip;
 167                 iser_state->is_instance = instance;
 168 
 169                 /* Initialize the open refcnt and it's lock */
 170                 iser_state->is_open_refcnt = 0;
 171                 mutex_init(&iser_state->is_refcnt_lock, NULL, MUTEX_DRIVER,
 172                     NULL);
 173 
 174                 iser_taskq = ddi_taskq_create(dip, "iser_taskq",
 175                     ISER_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
 176 
 177                 if (iser_taskq == NULL) {
 178                         ISER_LOG(CE_CONT, "%s%d: failed to create taskq",
 179                             "iser", instance);
 180                         mutex_destroy(&iser_state->is_refcnt_lock);
 181                         return (DDI_FAILURE);
 182                 }
 183 
 184                 /* initialize iSER as IB service */
 185                 status = iser_ib_init();
 186                 if (status != DDI_SUCCESS) {
 187                         ddi_taskq_destroy(iser_taskq);
 188                         mutex_destroy(&iser_state->is_refcnt_lock);
 189                         ISER_LOG(CE_CONT, "%s%d: failed to initialize IB",
 190                             "iser", instance);
 191                         return (DDI_FAILURE);
 192                 }
 193 
 194                 status = ddi_create_minor_node(
 195                     dip, ddi_get_name(dip), S_IFCHR, instance,
 196                     DDI_PSEUDO, 0);
 197                 if (status != DDI_SUCCESS) {
 198                         (void) iser_ib_fini();
 199                         ddi_taskq_destroy(iser_taskq);
 200                         mutex_destroy(&iser_state->is_refcnt_lock);
 201                         ISER_LOG(CE_CONT, "%s%d: failed ddi_create_minor_node",
 202                             "iser", instance);
 203                         return (DDI_FAILURE);
 204                 }
 205 
 206                 ddi_report_dev(dip);
 207 
 208                 return (DDI_SUCCESS);
 209 
 210         case DDI_RESUME:
 211                 ISER_LOG(CE_CONT, "iser_detach: DDI_RESUME unsupported");
 212                 return (DDI_FAILURE);
 213 
 214         default:
 215                 ISER_LOG(CE_CONT, "%s%d: unknown cmd in attach (0x%x)", "iser",
 216                     instance, cmd);
 217                 return (DDI_FAILURE);
 218         }
 219 }
 220 
 221 /*
 222  * iser_detach()
 223  */
 224 static int
 225 iser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 226 {
 227         mutex_enter(&iser_state->is_refcnt_lock);
 228         if (iser_state->is_open_refcnt > 0) {
 229                 mutex_exit(&iser_state->is_refcnt_lock);
 230                 return (DDI_FAILURE);
 231         }
 232         mutex_exit(&iser_state->is_refcnt_lock);
 233         mutex_destroy(&iser_state->is_refcnt_lock);
 234 
 235         switch (cmd) {
 236         case DDI_DETACH:
 237                 ISER_LOG(CE_CONT, "iser_detach: DDI_DETACH");
 238 
 239                 if (iser_ib_fini() != DDI_SUCCESS) {
 240                         ISER_LOG(CE_CONT, "iser_ib_fini failed");
 241                         return (DDI_FAILURE);
 242                 }
 243 
 244                 if (iser_taskq != NULL) {
 245                         ddi_taskq_destroy(iser_taskq);
 246                         iser_taskq = NULL;
 247                 }
 248                 ddi_remove_minor_node(dip, NULL);
 249 
 250                 return (DDI_SUCCESS);
 251 
 252         case DDI_SUSPEND:
 253                 ISER_LOG(CE_CONT, "iser_detach: DDI_SUSPEND unsupported");
 254                 return (DDI_FAILURE);
 255 
 256         default:
 257                 ISER_LOG(CE_CONT, "iser: unknown cmd in detach (0x%x)", cmd);
 258                 return (DDI_FAILURE);
 259         }
 260 }
 261 
 262 /*
 263  * iser_getinfo()
 264  */
 265 /* ARGSUSED */
 266 static int
 267 iser_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 268 {
 269         switch (cmd) {
 270         case DDI_INFO_DEVT2DEVINFO:
 271                 *result = (void *)iser_state->is_dip;
 272                 return (DDI_SUCCESS);
 273 
 274         case DDI_INFO_DEVT2INSTANCE:
 275                 *result = NULL;
 276                 return (DDI_SUCCESS);
 277 
 278         default:
 279                 return (DDI_FAILURE);
 280         }
 281 
 282 }
 283 
 284 /*
 285  * iser_open()
 286  */
 287 /* ARGSUSED */
 288 static int
 289 iser_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 290 {
 291         minor_t         instance;
 292         int             status;
 293 
 294         instance = getminor(*devp);
 295 
 296         /* Register the transport with IDM */
 297         status = iser_idm_register();
 298         if (status != DDI_SUCCESS) {
 299                 ISER_LOG(CE_CONT, "%s%d: failed to register with IDM",
 300                     "iser", instance);
 301                 return (ENXIO);
 302         }
 303 
 304         /* Increment our open refcnt */
 305         mutex_enter(&iser_state->is_refcnt_lock);
 306         iser_state->is_open_refcnt++;
 307         mutex_exit(&iser_state->is_refcnt_lock);
 308 
 309         return (DDI_SUCCESS);
 310 }
 311 
 312 /*
 313  * iser_close()
 314  */
 315 /* ARGSUSED */
 316 static int
 317 iser_close(dev_t devp, int flag, int otyp, cred_t *credp)
 318 {
 319         ASSERT(iser_state->is_open_refcnt != 0);
 320 
 321         mutex_enter(&iser_state->is_refcnt_lock);
 322         iser_state->is_open_refcnt--;
 323         mutex_exit(&iser_state->is_refcnt_lock);
 324 
 325         return (DDI_SUCCESS);
 326 }
 327 
 328 iser_status_t
 329 iser_register_service(idm_svc_t *idm_svc)
 330 {
 331 
 332         return (iser_ib_register_service(idm_svc));
 333 }
 334 
 335 iser_status_t
 336 iser_bind_service(idm_svc_t *idm_svc)
 337 {
 338 
 339         return (iser_ib_bind_service(idm_svc));
 340 }
 341 
 342 void
 343 iser_unbind_service(idm_svc_t *idm_svc)
 344 {
 345 
 346         iser_ib_unbind_service(idm_svc);
 347 }
 348 
 349 void
 350 iser_deregister_service(idm_svc_t *idm_svc)
 351 {
 352 
 353         iser_ib_deregister_service(idm_svc);
 354 }
 355 
 356 /*
 357  * iser_path_exists
 358  * This function takes in a pair of endpoints and determines if an iSER path
 359  * exists between the two. The actual path information (required for creating
 360  * a RC channel) is not returned, instead a boolean value indicating if a path
 361  * exists is returned.
 362  *
 363  * To use an implicit source, a value of NULL is allowed for laddr.
 364  */
 365 boolean_t
 366 iser_path_exists(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
 367 {
 368 
 369         ibt_ip_addr_t           remote_ip, local_ip;
 370         ibt_path_info_t         path;
 371         int                     status;
 372 
 373         iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
 374         iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
 375 
 376         status = iser_ib_get_paths(&local_ip, &remote_ip, &path, NULL);
 377 
 378         return ((status == IBT_SUCCESS) ? B_TRUE : B_FALSE);
 379 }
 380 
 381 /*
 382  * iser_channel_alloc
 383  * This function allocates a reliable communication channel between the
 384  * given endpoints.
 385  */
 386 iser_chan_t *
 387 iser_channel_alloc(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
 388 {
 389         ibt_ip_addr_t           remote_ip, local_ip;
 390 
 391         iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
 392         iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
 393 
 394         return (iser_ib_alloc_channel_pathlookup(&local_ip, &remote_ip));
 395 }
 396 
 397 /*
 398  * iser_channel_open
 399  * This function opens the already allocated communication channel between the
 400  * two endpoints.
 401  */
 402 iser_status_t
 403 iser_channel_open(iser_chan_t *chan)
 404 {
 405         return (iser_ib_open_rc_channel(chan));
 406 }
 407 
 408 /*
 409  * iser_channel_close
 410  * This function closes the already opened communication channel between the
 411  * two endpoints.
 412  */
 413 void
 414 iser_channel_close(iser_chan_t *chan)
 415 {
 416         iser_ib_close_rc_channel(chan);
 417 }
 418 
 419 /*
 420  * iser_channel_free
 421  * This function frees the channel between the given endpoints
 422  */
 423 void
 424 iser_channel_free(iser_chan_t *chan)
 425 {
 426         iser_ib_free_rc_channel(chan);
 427 }
 428 
 429 /* ARGSUSED */
 430 static int
 431 iser_ioctl(dev_t devp, int cmd, intptr_t arg, int mode, cred_t *credp,
 432     int *rvalp)
 433 {
 434         return (DDI_SUCCESS);
 435 }