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