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