Home | History | Annotate | Download | only in targets
      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
    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
    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
    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
    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
    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, NULL) == 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
    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
    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
    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
    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
    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
    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
    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