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 }