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 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 
  28 /*
  29  * SMP - Serial Management Protocol Device Driver
  30  *
  31  * The SMP driver provides user programs access to SAS Serial Management
  32  * Protocol devices by providing ioctl interface.
  33  */
  34 
  35 #include <sys/modctl.h>
  36 #include <sys/file.h>
  37 #include <sys/scsi/scsi.h>
  38 #include <sys/scsi/targets/smp.h>
  39 #include <sys/sdt.h>
  40 
  41 /*
  42  * Standard entrypoints
  43  */
  44 static int smp_attach(dev_info_t *, ddi_attach_cmd_t);
  45 static int smp_detach(dev_info_t *, ddi_detach_cmd_t);
  46 static int smp_open(dev_t *, int, int, cred_t *);
  47 static int smp_close(dev_t, int, int, cred_t *);
  48 static int smp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  49 
  50 /*
  51  * Configuration routines
  52  */
  53 static int smp_do_attach(dev_info_t *);
  54 static int smp_do_detach(dev_info_t *);
  55 
  56 /*
  57  * Command handle routing
  58  */
  59 static int smp_handle_func(dev_t, intptr_t, int, cred_t *, int *);
  60 
  61 /*
  62  * Logging/debugging routines
  63  */
  64 static void smp_log(smp_state_t  *, int,  const char *, ...);
  65 
  66 int smp_retry_times     = SMP_DEFAULT_RETRY_TIMES;
  67 int smp_retry_delay     = 10000;        /* 10msec */
  68 int smp_delay_cmd       = 1;            /* 1usec */
  69 int smp_single_command  = 1;            /* one command at a time */
  70 
  71 static int smp_retry_recovered  = 0;    /* retry recovery counter */
  72 static int smp_retry_failed     = 0;    /* retry failed counter */
  73 static int smp_failed           = 0;
  74 
  75 static struct cb_ops smp_cb_ops = {
  76         smp_open,                       /* open */
  77         smp_close,                      /* close */
  78         nodev,                          /* strategy */
  79         nodev,                          /* print */
  80         nodev,                          /* dump */
  81         nodev,                          /* read */
  82         nodev,                          /* write */
  83         smp_ioctl,                      /* ioctl */
  84         nodev,                          /* devmap */
  85         nodev,                          /* mmap */
  86         nodev,                          /* segmap */
  87         nochpoll,                       /* poll */
  88         ddi_prop_op,                    /* cb_prop_op */
  89         0,                              /* streamtab  */
  90         D_MP | D_NEW | D_HOTPLUG        /* Driver compatibility flag */
  91 };
  92 
  93 static struct dev_ops smp_dev_ops = {
  94         DEVO_REV,               /* devo_rev, */
  95         0,                      /* refcnt  */
  96         ddi_getinfo_1to1,       /* info */
  97         nulldev,                /* identify */
  98         NULL,                   /* probe */
  99         smp_attach,             /* attach */
 100         smp_detach,             /* detach */
 101         nodev,                  /* reset */
 102         &smp_cb_ops,                /* driver operations */
 103         (struct bus_ops *)0,    /* bus operations */
 104         NULL,                   /* power */
 105         ddi_quiesce_not_needed,         /* quiesce */
 106 };
 107 
 108 static void *smp_soft_state = NULL;
 109 
 110 static struct modldrv modldrv = {
 111         &mod_driverops, "smp device driver", &smp_dev_ops
 112 };
 113 
 114 static struct modlinkage modlinkage = {
 115         MODREV_1, &modldrv, NULL
 116 };
 117 
 118 int
 119 _init(void)
 120 {
 121         int err;
 122 
 123         if ((err = ddi_soft_state_init(&smp_soft_state,
 124             sizeof (smp_state_t), SMP_ESTIMATED_NUM_DEVS)) != 0) {
 125                 return (err);
 126         }
 127 
 128         if ((err = mod_install(&modlinkage)) != 0) {
 129                 ddi_soft_state_fini(&smp_soft_state);
 130         }
 131 
 132         return (err);
 133 }
 134 
 135 int
 136 _fini(void)
 137 {
 138         int err;
 139 
 140         if ((err = mod_remove(&modlinkage)) == 0) {
 141                 ddi_soft_state_fini(&smp_soft_state);
 142         }
 143 
 144         return (err);
 145 }
 146 
 147 int
 148 _info(struct modinfo *modinfop)
 149 {
 150         return (mod_info(&modlinkage, modinfop));
 151 }
 152 
 153 /*
 154  * smp_attach()
 155  *      attach(9e) entrypoint.
 156  */
 157 static int
 158 smp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 159 {
 160         int err;
 161 
 162         switch (cmd) {
 163         case DDI_ATTACH:
 164                 err = smp_do_attach(dip);
 165                 break;
 166         case DDI_RESUME:
 167                 err = DDI_SUCCESS;
 168                 break;
 169         default:
 170                 err = DDI_FAILURE;
 171                 break;
 172         }
 173 
 174         if (err != DDI_SUCCESS) {
 175                 smp_log(NULL, CE_NOTE, "!smp_attach(), "
 176                     "device unit-address @%s failed",
 177                     ddi_get_name_addr(dip));
 178         }
 179         return (err);
 180 }
 181 
 182 /*
 183  * smp_do_attach()
 184  *      handle the nitty details of attach.
 185  */
 186 static int
 187 smp_do_attach(dev_info_t *dip)
 188 {
 189         int                     instance;
 190         struct smp_device       *smp_sd;
 191         uchar_t                 *srmir = NULL;
 192         uint_t                  srmirlen = 0;
 193         ddi_devid_t             devid = NULL;
 194         smp_state_t             *smp_state;
 195 
 196         instance = ddi_get_instance(dip);
 197         smp_sd = ddi_get_driver_private(dip);
 198         ASSERT(smp_sd != NULL);
 199 
 200         DTRACE_PROBE2(smp__attach__detach, int, instance, char *,
 201             ddi_get_name_addr(dip));
 202 
 203         /* make sure device is there, and establish srmir identity property */
 204         if (smp_probe(smp_sd) != DDI_PROBE_SUCCESS) {
 205                 smp_log(NULL, CE_NOTE,
 206                     "!smp_do_attach: failed smp_probe, "
 207                     "device unit-address @%s", ddi_get_name_addr(dip));
 208                 return (DDI_FAILURE);
 209         }
 210 
 211         /* if we have not already registered a devid, then do so now  */
 212         if (ddi_devid_get(dip, &devid) != DDI_SUCCESS) {
 213                 /* get the srmir identity information for use in devid */
 214                 (void) ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
 215                     DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
 216                     SMP_PROP_REPORT_MANUFACTURER, &srmir, &srmirlen);
 217 
 218                 /* Convert smp unit-address and srmir into devid */
 219                 if (ddi_devid_smp_encode(DEVID_SMP_ENCODE_VERSION_LATEST,
 220                     (char *)ddi_driver_name(dip), ddi_get_name_addr(dip),
 221                     srmir, srmirlen, &devid) == DDI_SUCCESS) {
 222                         /* register the devid */
 223                         (void) ddi_devid_register(dip, devid);
 224                 }
 225                 ddi_prop_free(srmir);
 226         }
 227 
 228         /* We don't need the devid for our own operation, so free now. */
 229         if (devid)
 230                 ddi_devid_free(devid);
 231 
 232         /* we are now done with srmir identity property defined by smp_probe */
 233         (void) ndi_prop_remove(DDI_DEV_T_NONE,
 234             dip, SMP_PROP_REPORT_MANUFACTURER);
 235 
 236         if (ddi_soft_state_zalloc(smp_soft_state, instance) != DDI_SUCCESS) {
 237                 smp_log(NULL, CE_NOTE,
 238                     "!smp_do_attach: failed to allocate softstate, "
 239                     "device unit-address @%s", ddi_get_name_addr(dip));
 240                 return (DDI_FAILURE);
 241         }
 242 
 243         smp_state = ddi_get_soft_state(smp_soft_state, instance);
 244         smp_state->smp_sd = smp_sd;
 245 
 246         /*
 247          * For simplicity, the minor number == the instance number
 248          */
 249         if (ddi_create_minor_node(dip, "smp", S_IFCHR,
 250             instance, DDI_NT_SMP, NULL) == DDI_FAILURE) {
 251                 smp_log(smp_state, CE_NOTE,
 252                     "!smp_do_attach: minor node creation failed, "
 253                     "device unit-address @%s", ddi_get_name_addr(dip));
 254                 ddi_soft_state_free(smp_soft_state, instance);
 255                 return (DDI_FAILURE);
 256         }
 257 
 258         mutex_init(&smp_state->smp_mutex, NULL, MUTEX_DRIVER, NULL);
 259         smp_state->smp_open_flag = SMP_CLOSED;
 260 
 261         ddi_report_dev(dip);
 262         return (DDI_SUCCESS);
 263 }
 264 
 265 /*
 266  * smp_detach()
 267  *      detach(9E) entrypoint
 268  */
 269 static int
 270 smp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 271 {
 272         int instance;
 273         smp_state_t *smp_state;
 274 
 275         instance = ddi_get_instance(dip);
 276         smp_state = ddi_get_soft_state(smp_soft_state, instance);
 277 
 278         if (smp_state == NULL) {
 279                 smp_log(NULL, CE_NOTE,
 280                     "!smp_detach: failed, no softstate found (%d), "
 281                     "device unit-address @%s",
 282                     instance, ddi_get_name_addr(dip));
 283                 return (DDI_FAILURE);
 284         }
 285 
 286         switch (cmd) {
 287         case DDI_DETACH:
 288                 return (smp_do_detach(dip));
 289         case DDI_SUSPEND:
 290                 return (DDI_SUCCESS);
 291         default:
 292                 return (DDI_FAILURE);
 293         }
 294 }
 295 
 296 /*
 297  * smp_do_detach()
 298  *      detach the driver, tearing down resources.
 299  */
 300 static int
 301 smp_do_detach(dev_info_t *dip)
 302 {
 303         int instance;
 304         smp_state_t *smp_state;
 305 
 306         instance = ddi_get_instance(dip);
 307         smp_state = ddi_get_soft_state(smp_soft_state, instance);
 308 
 309         DTRACE_PROBE2(smp__attach__detach, int, instance, char *,
 310             ddi_get_name_addr(dip));
 311 
 312         mutex_destroy(&smp_state->smp_mutex);
 313         ddi_soft_state_free(smp_soft_state, instance);
 314         ddi_remove_minor_node(dip, NULL);
 315         return (DDI_SUCCESS);
 316 }
 317 
 318 /*ARGSUSED*/
 319 static int
 320 smp_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
 321 {
 322         smp_state_t *smp_state;
 323         int instance;
 324         int rv = 0;
 325 
 326         instance = getminor(*dev_p);
 327         if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
 328             == NULL) {
 329                 return (ENXIO);
 330         }
 331 
 332         mutex_enter(&smp_state->smp_mutex);
 333         if (flag & FEXCL) {
 334                 if (smp_state->smp_open_flag != SMP_CLOSED) {
 335                         rv = EBUSY;
 336                 } else {
 337                         smp_state->smp_open_flag = SMP_EXOPENED;
 338                 }
 339         } else {
 340                 if (smp_state->smp_open_flag == SMP_EXOPENED) {
 341                         rv = EBUSY;
 342                 } else {
 343                         smp_state->smp_open_flag = SMP_SOPENED;
 344                 }
 345         }
 346         mutex_exit(&smp_state->smp_mutex);
 347 
 348         return (rv);
 349 }
 350 
 351 /*ARGSUSED*/
 352 static int
 353 smp_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
 354 {
 355         smp_state_t *smp_state;
 356         int instance;
 357         int rv = 0;
 358 
 359         instance = getminor(dev);
 360         if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
 361             == NULL) {
 362                 return (ENXIO);
 363         }
 364 
 365         mutex_enter(&smp_state->smp_mutex);
 366         if (smp_state->smp_open_flag == SMP_CLOSED) {
 367                 smp_log(smp_state, CE_NOTE, "!smp device is already in close");
 368         } else {
 369                 smp_state->smp_open_flag = SMP_CLOSED;
 370         }
 371         mutex_exit(&smp_state->smp_mutex);
 372         return (rv);
 373 }
 374 
 375 /*ARGSUSED*/
 376 static int
 377 smp_handle_func(dev_t dev,
 378     intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
 379 {
 380         usmp_cmd_t usmp_cmd_data, *usmp_cmd = &usmp_cmd_data;
 381         smp_pkt_t smp_pkt_data, *smp_pkt = &smp_pkt_data;
 382         smp_state_t *smp_state;
 383         int instance, retrycount;
 384         cred_t *cr;
 385         uint64_t cmd_flags = 0;
 386         int rval = 0;
 387 
 388 #ifdef  _MULTI_DATAMODEL
 389         usmp_cmd32_t usmp_cmd32_data, *usmp_cmd32 = &usmp_cmd32_data;
 390 #endif
 391 
 392         /* require PRIV_SYS_DEVICES privilege */
 393         cr = ddi_get_cred();
 394         if ((drv_priv(cred_p) != 0) && (drv_priv(cr) != 0)) {
 395                 return (EPERM);
 396         }
 397 
 398         bzero(smp_pkt, sizeof (smp_pkt_t));
 399 
 400         instance = getminor(dev);
 401         if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
 402             == NULL) {
 403                 return (ENXIO);
 404         }
 405 
 406 #ifdef  _MULTI_DATAMODEL
 407         switch (ddi_model_convert_from(flag & FMODELS)) {
 408         case DDI_MODEL_ILP32:
 409                 if (ddi_copyin((void *)arg, usmp_cmd32, sizeof (usmp_cmd32_t),
 410                     flag)) {
 411                         return (EFAULT);
 412                 }
 413 
 414                 usmp_cmd32tousmp_cmd(usmp_cmd32, usmp_cmd);
 415                 break;
 416         case DDI_MODEL_NONE:
 417                 if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t),
 418                     flag)) {
 419                         return (EFAULT);
 420                 }
 421                 break;
 422         }
 423 #else  /* ! _MULTI_DATAMODEL */
 424         if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), flag)) {
 425                 return (EFAULT);
 426         }
 427 #endif  /* _MULTI_DATAMODEL */
 428 
 429         if ((usmp_cmd->usmp_reqsize < SMP_MIN_REQUEST_SIZE) ||
 430             (usmp_cmd->usmp_reqsize > SMP_MAX_REQUEST_SIZE) ||
 431             (usmp_cmd->usmp_rspsize < SMP_MIN_RESPONSE_SIZE) ||
 432             (usmp_cmd->usmp_rspsize > SMP_MAX_RESPONSE_SIZE)) {
 433                 rval = EINVAL;
 434                 goto done;
 435         }
 436 
 437         smp_pkt->smp_pkt_reqsize = usmp_cmd->usmp_reqsize;
 438         smp_pkt->smp_pkt_rspsize = usmp_cmd->usmp_rspsize;
 439 
 440         /* allocate memory space for smp request and response frame in kernel */
 441         smp_pkt->smp_pkt_req = kmem_zalloc((size_t)usmp_cmd->usmp_reqsize,
 442             KM_SLEEP);
 443         cmd_flags |= SMP_FLAG_REQBUF;
 444 
 445         smp_pkt->smp_pkt_rsp = kmem_zalloc((size_t)usmp_cmd->usmp_rspsize,
 446             KM_SLEEP);
 447         cmd_flags |= SMP_FLAG_RSPBUF;
 448 
 449         /* copy smp request frame to kernel space */
 450         if (ddi_copyin(usmp_cmd->usmp_req, smp_pkt->smp_pkt_req,
 451             (size_t)usmp_cmd->usmp_reqsize, flag) != 0) {
 452                 rval = EFAULT;
 453                 goto done;
 454         }
 455 
 456         DTRACE_PROBE1(smp__transport__start, caddr_t, smp_pkt->smp_pkt_req);
 457 
 458         smp_pkt->smp_pkt_address = &smp_state->smp_sd->smp_sd_address;
 459         if (usmp_cmd->usmp_timeout <= 0) {
 460                 smp_pkt->smp_pkt_timeout = SMP_DEFAULT_TIMEOUT;
 461         } else {
 462                 smp_pkt->smp_pkt_timeout = usmp_cmd->usmp_timeout;
 463         }
 464 
 465         /* call smp_transport entry and send smp_pkt to HBA driver */
 466         cmd_flags |= SMP_FLAG_XFER;
 467         for (retrycount = 0; retrycount <= smp_retry_times; retrycount++) {
 468 
 469                 /*
 470                  * To improve transport reliability, only allow one command
 471                  * outstanding at a time in smp_transport().
 472                  *
 473                  * NOTE: Some expanders have issues with heavy smp load.
 474                  */
 475                 if (smp_single_command) {
 476                         mutex_enter(&smp_state->smp_mutex);
 477                         while (smp_state->smp_busy)
 478                                 cv_wait(&smp_state->smp_cv,
 479                                     &smp_state->smp_mutex);
 480                         smp_state->smp_busy = 1;
 481                         mutex_exit(&smp_state->smp_mutex);
 482                 }
 483 
 484                 /* Let the transport know if more retries are possible. */
 485                 smp_pkt->smp_pkt_will_retry =
 486                     (retrycount < smp_retry_times) ? 1 : 0;
 487 
 488                 smp_pkt->smp_pkt_reason = 0;
 489                 rval = smp_transport(smp_pkt);  /* put on the wire */
 490 
 491                 if (smp_delay_cmd)
 492                         delay(drv_usectohz(smp_delay_cmd));
 493 
 494                 if (smp_single_command) {
 495                         mutex_enter(&smp_state->smp_mutex);
 496                         smp_state->smp_busy = 0;
 497                         cv_signal(&smp_state->smp_cv);
 498                         mutex_exit(&smp_state->smp_mutex);
 499                 }
 500 
 501                 if (rval == DDI_SUCCESS) {
 502                         if (retrycount)
 503                                 smp_retry_recovered++;
 504                         rval = 0;
 505                         break;
 506                 }
 507 
 508                 switch (smp_pkt->smp_pkt_reason) {
 509                 case EAGAIN:
 510                         if (retrycount < smp_retry_times) {
 511                                 bzero(smp_pkt->smp_pkt_rsp,
 512                                     (size_t)usmp_cmd->usmp_rspsize);
 513                                 if (smp_retry_delay)
 514                                         delay(drv_usectohz(smp_retry_delay));
 515                                 continue;
 516                         } else {
 517                                 smp_retry_failed++;
 518                                 smp_log(smp_state, CE_NOTE,
 519                                     "!smp_transport failed, smp_pkt_reason %d",
 520                                     smp_pkt->smp_pkt_reason);
 521                                 rval = smp_pkt->smp_pkt_reason;
 522                                 goto copyout;
 523                         }
 524                 default:
 525                         smp_log(smp_state, CE_NOTE,
 526                             "!smp_transport failed, smp_pkt_reason %d",
 527                             smp_pkt->smp_pkt_reason);
 528                         rval = smp_pkt->smp_pkt_reason;
 529                         goto copyout;
 530                 }
 531         }
 532 
 533 copyout:
 534         /* copy out smp response to user process */
 535         if (ddi_copyout(smp_pkt->smp_pkt_rsp, usmp_cmd->usmp_rsp,
 536             (size_t)usmp_cmd->usmp_rspsize, flag) != 0) {
 537                 rval = EFAULT;
 538         }
 539 
 540 done:
 541         if ((cmd_flags & SMP_FLAG_XFER) != 0) {
 542                 DTRACE_PROBE2(smp__transport__done, caddr_t,
 543                     smp_pkt->smp_pkt_rsp, uchar_t, smp_pkt->smp_pkt_reason);
 544         }
 545         if ((cmd_flags & SMP_FLAG_REQBUF) != 0) {
 546                 kmem_free(smp_pkt->smp_pkt_req, smp_pkt->smp_pkt_reqsize);
 547         }
 548         if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) {
 549                 kmem_free(smp_pkt->smp_pkt_rsp, smp_pkt->smp_pkt_rspsize);
 550         }
 551 
 552         if (rval)
 553                 smp_failed++;
 554         return (rval);
 555 }
 556 
 557 /*ARGSUSED*/
 558 static int
 559 smp_ioctl(dev_t dev,
 560     int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
 561 {
 562         int rval = 0;
 563 
 564         switch (cmd) {
 565         case USMPFUNC:
 566                 /*
 567                  * The response payload is valid only if return value is 0
 568                  * or EOVERFLOW.
 569                  */
 570                 rval = smp_handle_func(dev, arg, flag, cred_p, rval_p);
 571                 break;
 572         default:
 573                 rval = EINVAL;
 574         }
 575         return (rval);
 576 }
 577 
 578 static void
 579 smp_log(smp_state_t *smp_state, int level, const char *fmt, ...)
 580 {
 581         va_list ap;
 582         char buf[256];
 583         dev_info_t *dip;
 584 
 585         if (smp_state == (smp_state_t *)NULL) {
 586                 dip = NULL;
 587         } else {
 588                 dip = smp_state->smp_sd->smp_sd_dev;
 589         }
 590 
 591         va_start(ap, fmt);
 592         (void) vsnprintf(buf, sizeof (buf), fmt, ap);
 593         va_end(ap);
 594 
 595         scsi_log(dip, "smp", level, "%s", buf);
 596 }