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