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