xref: /illumos-gate/usr/src/uts/common/io/scsi/targets/smp.c (revision 5bbb4db2c3f208d12bf0fd11769728f9e5ba66a2)
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