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