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 2008 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 mutex_init(&smp_state->smp_mutex, NULL, MUTEX_DRIVER, NULL); 219 smp_state->smp_open_flag = SMP_CLOSED; 220 221 ddi_report_dev(dip); 222 return (DDI_SUCCESS); 223 } 224 225 /* 226 * smp_detach() 227 * detach(9E) entrypoint 228 */ 229 static int 230 smp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 231 { 232 int instance; 233 smp_state_t *smp_state; 234 235 instance = ddi_get_instance(dip); 236 smp_state = ddi_get_soft_state(smp_soft_state, instance); 237 238 if (smp_state == NULL) { 239 smp_log(NULL, CE_NOTE, 240 "!smp_detach: failed, no softstate found (%d), " 241 "device unit-address @%s", 242 instance, ddi_get_name_addr(dip)); 243 return (DDI_FAILURE); 244 } 245 246 switch (cmd) { 247 case DDI_DETACH: 248 return (smp_do_detach(dip)); 249 case DDI_SUSPEND: 250 return (DDI_SUCCESS); 251 default: 252 return (DDI_FAILURE); 253 } 254 } 255 256 /* 257 * smp_do_detach() 258 * detach the driver, tearing down resources. 259 */ 260 static int 261 smp_do_detach(dev_info_t *dip) 262 { 263 int instance; 264 smp_state_t *smp_state; 265 266 instance = ddi_get_instance(dip); 267 smp_state = ddi_get_soft_state(smp_soft_state, instance); 268 269 DTRACE_PROBE2(smp__attach__detach, int, instance, char *, 270 ddi_get_name_addr(dip)); 271 272 mutex_destroy(&smp_state->smp_mutex); 273 ddi_soft_state_free(smp_soft_state, instance); 274 ddi_remove_minor_node(dip, NULL); 275 return (DDI_SUCCESS); 276 } 277 278 static int 279 smp_probe(dev_info_t *dip) 280 { 281 struct smp_device *smpdevp; 282 283 smpdevp = ddi_get_driver_private(dip); 284 285 return (sas_hba_probe_smp(smpdevp)); 286 } 287 288 /* 289 * smp_getinfo() 290 * getinfo(9e) entrypoint. 291 */ 292 /*ARGSUSED*/ 293 static int 294 smp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 295 { 296 dev_t dev; 297 smp_state_t *smp_state; 298 int instance, error; 299 switch (infocmd) { 300 case DDI_INFO_DEVT2DEVINFO: 301 dev = (dev_t)arg; 302 instance = getminor(dev); 303 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 304 == NULL) 305 return (DDI_FAILURE); 306 *result = (void *) smp_state->smp_dev->dip; 307 error = DDI_SUCCESS; 308 break; 309 case DDI_INFO_DEVT2INSTANCE: 310 dev = (dev_t)arg; 311 instance = getminor(dev); 312 *result = (void *)(uintptr_t)instance; 313 error = DDI_SUCCESS; 314 break; 315 default: 316 error = DDI_FAILURE; 317 } 318 return (error); 319 } 320 321 /*ARGSUSED*/ 322 static int 323 smp_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) 324 { 325 smp_state_t *smp_state; 326 int instance; 327 int rv = 0; 328 329 instance = getminor(*dev_p); 330 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 331 == NULL) { 332 return (ENXIO); 333 } 334 335 mutex_enter(&smp_state->smp_mutex); 336 if (flag & FEXCL) { 337 if (smp_state->smp_open_flag != SMP_CLOSED) { 338 rv = EBUSY; 339 } else { 340 smp_state->smp_open_flag = SMP_EXOPENED; 341 } 342 } else { 343 if (smp_state->smp_open_flag == SMP_EXOPENED) { 344 rv = EBUSY; 345 } else { 346 smp_state->smp_open_flag = SMP_SOPENED; 347 } 348 } 349 mutex_exit(&smp_state->smp_mutex); 350 351 return (rv); 352 } 353 354 /*ARGSUSED*/ 355 static int 356 smp_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 357 { 358 smp_state_t *smp_state; 359 int instance; 360 int rv = 0; 361 362 instance = getminor(dev); 363 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 364 == NULL) { 365 return (ENXIO); 366 } 367 368 mutex_enter(&smp_state->smp_mutex); 369 if (smp_state->smp_open_flag == SMP_CLOSED) { 370 smp_log(smp_state, CE_NOTE, "!smp device is already in close"); 371 } else { 372 smp_state->smp_open_flag = SMP_CLOSED; 373 } 374 mutex_exit(&smp_state->smp_mutex); 375 return (rv); 376 } 377 378 /*ARGSUSED*/ 379 static int 380 smp_handle_func(dev_t dev, 381 intptr_t arg, int flag, cred_t *cred_p, int *rval_p) 382 { 383 usmp_cmd_t usmp_cmd_data, *usmp_cmd = &usmp_cmd_data; 384 smp_pkt_t smp_pkt_data, *smp_pkt = &smp_pkt_data; 385 smp_state_t *smp_state; 386 int instance, retrycount; 387 cred_t *cr; 388 uint64_t cmd_flags = 0; 389 int rval = 0; 390 391 #ifdef _MULTI_DATAMODEL 392 usmp_cmd32_t usmp_cmd32_data, *usmp_cmd32 = &usmp_cmd32_data; 393 #endif 394 395 /* require PRIV_SYS_DEVICES privilege */ 396 cr = ddi_get_cred(); 397 if ((drv_priv(cred_p) != 0) && (drv_priv(cr) != 0)) { 398 return (EPERM); 399 } 400 401 bzero(smp_pkt, sizeof (smp_pkt_t)); 402 403 instance = getminor(dev); 404 if ((smp_state = ddi_get_soft_state(smp_soft_state, instance)) 405 == NULL) { 406 return (ENXIO); 407 } 408 409 #ifdef _MULTI_DATAMODEL 410 switch (ddi_model_convert_from(flag & FMODELS)) { 411 case DDI_MODEL_ILP32: 412 if (ddi_copyin((void *)arg, usmp_cmd32, sizeof (usmp_cmd32_t), 413 flag)) { 414 return (EFAULT); 415 } 416 417 usmp_cmd32tousmp_cmd(usmp_cmd32, usmp_cmd); 418 break; 419 case DDI_MODEL_NONE: 420 if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), 421 flag)) { 422 return (EFAULT); 423 } 424 break; 425 } 426 #else /* ! _MULTI_DATAMODEL */ 427 if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), flag)) { 428 return (EFAULT); 429 } 430 #endif /* _MULTI_DATAMODEL */ 431 432 if ((usmp_cmd->usmp_reqsize < SMP_MIN_REQUEST_SIZE) || 433 (usmp_cmd->usmp_reqsize > SMP_MAX_REQUEST_SIZE) || 434 (usmp_cmd->usmp_rspsize < SMP_MIN_RESPONSE_SIZE) || 435 (usmp_cmd->usmp_rspsize > SMP_MAX_RESPONSE_SIZE)) { 436 rval = EINVAL; 437 goto done; 438 } 439 440 smp_pkt->pkt_reqsize = usmp_cmd->usmp_reqsize; 441 smp_pkt->pkt_rspsize = usmp_cmd->usmp_rspsize; 442 443 /* allocate memory space for smp request and response frame in kernel */ 444 smp_pkt->pkt_req = kmem_zalloc((size_t)usmp_cmd->usmp_reqsize, 445 KM_SLEEP); 446 cmd_flags |= SMP_FLAG_REQBUF; 447 448 smp_pkt->pkt_rsp = kmem_zalloc((size_t)usmp_cmd->usmp_rspsize, 449 KM_SLEEP); 450 cmd_flags |= SMP_FLAG_RSPBUF; 451 452 /* copy smp request frame to kernel space */ 453 if (ddi_copyin(usmp_cmd->usmp_req, smp_pkt->pkt_req, 454 (size_t)usmp_cmd->usmp_reqsize, flag) != 0) { 455 rval = EFAULT; 456 goto done; 457 } 458 459 DTRACE_PROBE1(smp__transport__start, caddr_t, smp_pkt->pkt_req); 460 461 smp_pkt->pkt_address = &smp_state->smp_dev->smp_addr; 462 smp_pkt->pkt_reason = 0; 463 if (usmp_cmd->usmp_timeout <= 0) { 464 smp_pkt->pkt_timeout = SMP_DEFAULT_TIMEOUT; 465 } else { 466 smp_pkt->pkt_timeout = usmp_cmd->usmp_timeout; 467 } 468 469 /* call sas_smp_transport entry and send smp_pkt to HBA driver */ 470 cmd_flags |= SMP_FLAG_XFER; 471 for (retrycount = 0; retrycount <= smp_retry_times; retrycount++) { 472 if (sas_smp_transport(smp_pkt) == DDI_SUCCESS) { 473 rval = DDI_SUCCESS; 474 break; 475 } 476 477 switch (smp_pkt->pkt_reason) { 478 case EAGAIN: 479 if (retrycount < smp_retry_times) { 480 smp_pkt->pkt_reason = 0; 481 bzero(smp_pkt->pkt_rsp, 482 (size_t)usmp_cmd->usmp_rspsize); 483 delay(drv_usectohz(10000)); /* 10 ms */ 484 continue; 485 } else { 486 smp_log(smp_state, CE_NOTE, 487 "!sas_smp_transport failed, pkt_reason %d", 488 smp_pkt->pkt_reason); 489 rval = smp_pkt->pkt_reason; 490 goto copyout; 491 } 492 default: 493 smp_log(smp_state, CE_NOTE, 494 "!sas_smp_transport failed, pkt_reason %d", 495 smp_pkt->pkt_reason); 496 rval = smp_pkt->pkt_reason; 497 goto copyout; 498 } 499 } 500 501 copyout: 502 /* copy out smp response to user process */ 503 if (ddi_copyout(smp_pkt->pkt_rsp, usmp_cmd->usmp_rsp, 504 (size_t)usmp_cmd->usmp_rspsize, flag) != 0) { 505 rval = EFAULT; 506 } 507 508 done: 509 if ((cmd_flags & SMP_FLAG_XFER) != 0) { 510 DTRACE_PROBE2(smp__transport__done, caddr_t, smp_pkt->pkt_rsp, 511 uchar_t, smp_pkt->pkt_reason); 512 } 513 if ((cmd_flags & SMP_FLAG_REQBUF) != 0) { 514 kmem_free(smp_pkt->pkt_req, smp_pkt->pkt_reqsize); 515 } 516 if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) { 517 kmem_free(smp_pkt->pkt_rsp, smp_pkt->pkt_rspsize); 518 } 519 return (rval); 520 } 521 522 /*ARGSUSED*/ 523 static int 524 smp_ioctl(dev_t dev, 525 int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p) 526 { 527 int rval = 0; 528 529 switch (cmd) { 530 case USMPFUNC: 531 /* 532 * The response payload is valid only if return value is 0 533 * or EOVERFLOW. 534 */ 535 rval = smp_handle_func(dev, arg, flag, cred_p, rval_p); 536 break; 537 default: 538 rval = EINVAL; 539 } 540 return (rval); 541 } 542 543 static void 544 smp_log(smp_state_t *smp_state, int level, const char *fmt, ...) 545 { 546 va_list ap; 547 char buf[256]; 548 dev_info_t *dip; 549 550 if (smp_state == (smp_state_t *)NULL) { 551 dip = NULL; 552 } else { 553 dip = smp_state->smp_dev->dip; 554 } 555 556 va_start(ap, fmt); 557 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 558 va_end(ap); 559 560 scsi_log(dip, "smp", level, "%s", buf); 561 } 562