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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
smp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
smp_do_attach(dev_info_t * dip)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, 0) == 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
smp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
smp_do_detach(dev_info_t * dip)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
smp_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)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
smp_close(dev_t dev,int flag,int otyp,cred_t * cred_p)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
smp_handle_func(dev_t dev,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)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
smp_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)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
smp_log(smp_state_t * smp_state,int level,const char * fmt,...)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 }
597