xref: /onnv-gate/usr/src/uts/common/io/scsi/targets/smp.c (revision 11052:f59c5298a2cc)
15754Sjw149990 /*
25754Sjw149990  * CDDL HEADER START
35754Sjw149990  *
45754Sjw149990  * The contents of this file are subject to the terms of the
55754Sjw149990  * Common Development and Distribution License (the "License").
65754Sjw149990  * You may not use this file except in compliance with the License.
75754Sjw149990  *
85754Sjw149990  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95754Sjw149990  * or http://www.opensolaris.org/os/licensing.
105754Sjw149990  * See the License for the specific language governing permissions
115754Sjw149990  * and limitations under the License.
125754Sjw149990  *
135754Sjw149990  * When distributing Covered Code, include this CDDL HEADER in each
145754Sjw149990  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155754Sjw149990  * If applicable, add the following below this CDDL HEADER, with the
165754Sjw149990  * fields enclosed by brackets "[]" replaced with your own identifying
175754Sjw149990  * information: Portions Copyright [yyyy] [name of copyright owner]
185754Sjw149990  *
195754Sjw149990  * CDDL HEADER END
205754Sjw149990  */
215754Sjw149990 
225754Sjw149990 /*
2310696SDavid.Hollister@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
245754Sjw149990  * Use is subject to license terms.
255754Sjw149990  */
265754Sjw149990 
275754Sjw149990 
285754Sjw149990 /*
295754Sjw149990  * SMP - Serial Management Protocol Device Driver
305754Sjw149990  *
315754Sjw149990  * The SMP driver provides user programs access to SAS Serial Management
325754Sjw149990  * Protocol devices by providing ioctl interface.
335754Sjw149990  */
345754Sjw149990 
355754Sjw149990 #include <sys/modctl.h>
365754Sjw149990 #include <sys/file.h>
375754Sjw149990 #include <sys/scsi/scsi.h>
385754Sjw149990 #include <sys/scsi/targets/smp.h>
395754Sjw149990 #include <sys/sdt.h>
405754Sjw149990 
415754Sjw149990 /*
425754Sjw149990  * Standard entrypoints
435754Sjw149990  */
445754Sjw149990 static int smp_attach(dev_info_t *, ddi_attach_cmd_t);
455754Sjw149990 static int smp_detach(dev_info_t *, ddi_detach_cmd_t);
465754Sjw149990 static int smp_open(dev_t *, int, int, cred_t *);
475754Sjw149990 static int smp_close(dev_t, int, int, cred_t *);
485754Sjw149990 static int smp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
495754Sjw149990 
505754Sjw149990 /*
515754Sjw149990  * Configuration routines
525754Sjw149990  */
535754Sjw149990 static int smp_do_attach(dev_info_t *);
545754Sjw149990 static int smp_do_detach(dev_info_t *);
555754Sjw149990 
565754Sjw149990 /*
575754Sjw149990  * Command handle routing
585754Sjw149990  */
595754Sjw149990 static int smp_handle_func(dev_t, intptr_t, int, cred_t *, int *);
605754Sjw149990 
615754Sjw149990 /*
625754Sjw149990  * Logging/debugging routines
635754Sjw149990  */
645754Sjw149990 static void smp_log(smp_state_t  *, int,  const char *, ...);
655754Sjw149990 
6610696SDavid.Hollister@Sun.COM int smp_retry_times	= SMP_DEFAULT_RETRY_TIMES;
6710696SDavid.Hollister@Sun.COM int smp_retry_delay	= 10000;	/* 10msec */
6810696SDavid.Hollister@Sun.COM int smp_delay_cmd	= 1;		/* 1usec */
6910696SDavid.Hollister@Sun.COM int smp_single_command	= 1;		/* one command at a time */
7010696SDavid.Hollister@Sun.COM 
7110696SDavid.Hollister@Sun.COM static int smp_retry_recovered	= 0;	/* retry recovery counter */
7210696SDavid.Hollister@Sun.COM static int smp_retry_failed	= 0;	/* retry failed counter */
7310696SDavid.Hollister@Sun.COM static int smp_failed		= 0;
745754Sjw149990 
755754Sjw149990 static struct cb_ops smp_cb_ops = {
765754Sjw149990 	smp_open,			/* open */
775754Sjw149990 	smp_close,			/* close */
785754Sjw149990 	nodev,				/* strategy */
795754Sjw149990 	nodev,				/* print */
805754Sjw149990 	nodev,				/* dump */
815754Sjw149990 	nodev,				/* read */
825754Sjw149990 	nodev,				/* write */
835754Sjw149990 	smp_ioctl,			/* ioctl */
845754Sjw149990 	nodev,				/* devmap */
855754Sjw149990 	nodev,				/* mmap */
865754Sjw149990 	nodev,				/* segmap */
875754Sjw149990 	nochpoll,			/* poll */
885754Sjw149990 	ddi_prop_op,			/* cb_prop_op */
895754Sjw149990 	0,				/* streamtab  */
905754Sjw149990 	D_MP | D_NEW | D_HOTPLUG	/* Driver compatibility flag */
915754Sjw149990 };
925754Sjw149990 
935754Sjw149990 static struct dev_ops smp_dev_ops = {
945754Sjw149990 	DEVO_REV,		/* devo_rev, */
955754Sjw149990 	0,			/* refcnt  */
96*11052SChris.Horne@Sun.COM 	ddi_getinfo_1to1,	/* info */
975754Sjw149990 	nulldev,		/* identify */
98*11052SChris.Horne@Sun.COM 	NULL,			/* probe */
995754Sjw149990 	smp_attach,		/* attach */
1005754Sjw149990 	smp_detach,		/* detach */
1015754Sjw149990 	nodev,			/* reset */
1025754Sjw149990 	&smp_cb_ops,		/* driver operations */
1035754Sjw149990 	(struct bus_ops *)0,	/* bus operations */
1047656SSherry.Moore@Sun.COM 	NULL,			/* power */
1057656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1065754Sjw149990 };
1075754Sjw149990 
1085754Sjw149990 static void *smp_soft_state = NULL;
1095754Sjw149990 
1105754Sjw149990 static struct modldrv modldrv = {
1117656SSherry.Moore@Sun.COM 	&mod_driverops, "smp device driver", &smp_dev_ops
1125754Sjw149990 };
1135754Sjw149990 
1145754Sjw149990 static struct modlinkage modlinkage = {
1155754Sjw149990 	MODREV_1, &modldrv, NULL
1165754Sjw149990 };
1175754Sjw149990 
1185754Sjw149990 int
_init(void)1195754Sjw149990 _init(void)
1205754Sjw149990 {
1215754Sjw149990 	int err;
1225754Sjw149990 
1235754Sjw149990 	if ((err = ddi_soft_state_init(&smp_soft_state,
1245754Sjw149990 	    sizeof (smp_state_t), SMP_ESTIMATED_NUM_DEVS)) != 0) {
1255754Sjw149990 		return (err);
1265754Sjw149990 	}
1275754Sjw149990 
1285754Sjw149990 	if ((err = mod_install(&modlinkage)) != 0) {
1295754Sjw149990 		ddi_soft_state_fini(&smp_soft_state);
1305754Sjw149990 	}
1315754Sjw149990 
1325754Sjw149990 	return (err);
1335754Sjw149990 }
1345754Sjw149990 
1355754Sjw149990 int
_fini(void)1365754Sjw149990 _fini(void)
1375754Sjw149990 {
1385754Sjw149990 	int err;
1395754Sjw149990 
1405754Sjw149990 	if ((err = mod_remove(&modlinkage)) == 0) {
1415754Sjw149990 		ddi_soft_state_fini(&smp_soft_state);
1425754Sjw149990 	}
1435754Sjw149990 
1445754Sjw149990 	return (err);
1455754Sjw149990 }
1465754Sjw149990 
1475754Sjw149990 int
_info(struct modinfo * modinfop)1485754Sjw149990 _info(struct modinfo *modinfop)
1495754Sjw149990 {
1505754Sjw149990 	return (mod_info(&modlinkage, modinfop));
1515754Sjw149990 }
1525754Sjw149990 
1535754Sjw149990 /*
1545754Sjw149990  * smp_attach()
15510696SDavid.Hollister@Sun.COM  *	attach(9e) entrypoint.
1565754Sjw149990  */
1575754Sjw149990 static int
smp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1585754Sjw149990 smp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1595754Sjw149990 {
1605754Sjw149990 	int err;
1615754Sjw149990 
1625754Sjw149990 	switch (cmd) {
1635754Sjw149990 	case DDI_ATTACH:
1645754Sjw149990 		err = smp_do_attach(dip);
1655754Sjw149990 		break;
1665754Sjw149990 	case DDI_RESUME:
1675754Sjw149990 		err = DDI_SUCCESS;
1685754Sjw149990 		break;
1695754Sjw149990 	default:
1705754Sjw149990 		err = DDI_FAILURE;
1715754Sjw149990 		break;
1725754Sjw149990 	}
1735754Sjw149990 
1745754Sjw149990 	if (err != DDI_SUCCESS) {
1755754Sjw149990 		smp_log(NULL, CE_NOTE, "!smp_attach(), "
1765754Sjw149990 		    "device unit-address @%s failed",
1775754Sjw149990 		    ddi_get_name_addr(dip));
1785754Sjw149990 	}
1795754Sjw149990 	return (err);
1805754Sjw149990 }
1815754Sjw149990 
1825754Sjw149990 /*
1835754Sjw149990  * smp_do_attach()
1845754Sjw149990  *	handle the nitty details of attach.
1855754Sjw149990  */
1865754Sjw149990 static int
smp_do_attach(dev_info_t * dip)1875754Sjw149990 smp_do_attach(dev_info_t *dip)
1885754Sjw149990 {
189*11052SChris.Horne@Sun.COM 	int			instance;
190*11052SChris.Horne@Sun.COM 	struct smp_device	*smp_sd;
191*11052SChris.Horne@Sun.COM 	uchar_t			*srmir = NULL;
192*11052SChris.Horne@Sun.COM 	uint_t			srmirlen = 0;
193*11052SChris.Horne@Sun.COM 	ddi_devid_t		devid = NULL;
194*11052SChris.Horne@Sun.COM 	smp_state_t		*smp_state;
1955754Sjw149990 
1965754Sjw149990 	instance = ddi_get_instance(dip);
197*11052SChris.Horne@Sun.COM 	smp_sd = ddi_get_driver_private(dip);
198*11052SChris.Horne@Sun.COM 	ASSERT(smp_sd != NULL);
1995754Sjw149990 
2005754Sjw149990 	DTRACE_PROBE2(smp__attach__detach, int, instance, char *,
2015754Sjw149990 	    ddi_get_name_addr(dip));
2025754Sjw149990 
203*11052SChris.Horne@Sun.COM 	/* make sure device is there, and establish srmir identity property */
204*11052SChris.Horne@Sun.COM 	if (smp_probe(smp_sd) != DDI_PROBE_SUCCESS) {
205*11052SChris.Horne@Sun.COM 		smp_log(NULL, CE_NOTE,
206*11052SChris.Horne@Sun.COM 		    "!smp_do_attach: failed smp_probe, "
207*11052SChris.Horne@Sun.COM 		    "device unit-address @%s", ddi_get_name_addr(dip));
208*11052SChris.Horne@Sun.COM 		return (DDI_FAILURE);
209*11052SChris.Horne@Sun.COM 	}
210*11052SChris.Horne@Sun.COM 
211*11052SChris.Horne@Sun.COM 	/* if we have not already registered a devid, then do so now  */
212*11052SChris.Horne@Sun.COM 	if (ddi_devid_get(dip, &devid) != DDI_SUCCESS) {
213*11052SChris.Horne@Sun.COM 		/* get the srmir identity information for use in devid */
214*11052SChris.Horne@Sun.COM 		(void) ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
215*11052SChris.Horne@Sun.COM 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
216*11052SChris.Horne@Sun.COM 		    SMP_PROP_REPORT_MANUFACTURER, &srmir, &srmirlen);
217*11052SChris.Horne@Sun.COM 
218*11052SChris.Horne@Sun.COM 		/* Convert smp unit-address and srmir into devid */
219*11052SChris.Horne@Sun.COM 		if (ddi_devid_smp_encode(DEVID_SMP_ENCODE_VERSION_LATEST,
220*11052SChris.Horne@Sun.COM 		    (char *)ddi_driver_name(dip), ddi_get_name_addr(dip),
221*11052SChris.Horne@Sun.COM 		    srmir, srmirlen, &devid) == DDI_SUCCESS) {
222*11052SChris.Horne@Sun.COM 			/* register the devid */
223*11052SChris.Horne@Sun.COM 			(void) ddi_devid_register(dip, devid);
224*11052SChris.Horne@Sun.COM 		}
225*11052SChris.Horne@Sun.COM 		ddi_prop_free(srmir);
226*11052SChris.Horne@Sun.COM 	}
227*11052SChris.Horne@Sun.COM 
228*11052SChris.Horne@Sun.COM 	/* We don't need the devid for our own operation, so free now. */
229*11052SChris.Horne@Sun.COM 	if (devid)
230*11052SChris.Horne@Sun.COM 		ddi_devid_free(devid);
231*11052SChris.Horne@Sun.COM 
232*11052SChris.Horne@Sun.COM 	/* we are now done with srmir identity property defined by smp_probe */
233*11052SChris.Horne@Sun.COM 	(void) ndi_prop_remove(DDI_DEV_T_NONE,
234*11052SChris.Horne@Sun.COM 	    dip, SMP_PROP_REPORT_MANUFACTURER);
235*11052SChris.Horne@Sun.COM 
2365754Sjw149990 	if (ddi_soft_state_zalloc(smp_soft_state, instance) != DDI_SUCCESS) {
2375754Sjw149990 		smp_log(NULL, CE_NOTE,
2385754Sjw149990 		    "!smp_do_attach: failed to allocate softstate, "
2395754Sjw149990 		    "device unit-address @%s", ddi_get_name_addr(dip));
2405754Sjw149990 		return (DDI_FAILURE);
2415754Sjw149990 	}
2425754Sjw149990 
2435754Sjw149990 	smp_state = ddi_get_soft_state(smp_soft_state, instance);
244*11052SChris.Horne@Sun.COM 	smp_state->smp_sd = smp_sd;
2455754Sjw149990 
2465754Sjw149990 	/*
2475754Sjw149990 	 * For simplicity, the minor number == the instance number
2485754Sjw149990 	 */
2495754Sjw149990 	if (ddi_create_minor_node(dip, "smp", S_IFCHR,
2505754Sjw149990 	    instance, DDI_NT_SMP, NULL) == DDI_FAILURE) {
2515754Sjw149990 		smp_log(smp_state, CE_NOTE,
2525754Sjw149990 		    "!smp_do_attach: minor node creation failed, "
2535754Sjw149990 		    "device unit-address @%s", ddi_get_name_addr(dip));
2545754Sjw149990 		ddi_soft_state_free(smp_soft_state, instance);
2555754Sjw149990 		return (DDI_FAILURE);
2565754Sjw149990 	}
2575754Sjw149990 
2585754Sjw149990 	mutex_init(&smp_state->smp_mutex, NULL, MUTEX_DRIVER, NULL);
2595754Sjw149990 	smp_state->smp_open_flag = SMP_CLOSED;
2605754Sjw149990 
2615754Sjw149990 	ddi_report_dev(dip);
2625754Sjw149990 	return (DDI_SUCCESS);
2635754Sjw149990 }
2645754Sjw149990 
2655754Sjw149990 /*
2665754Sjw149990  * smp_detach()
26710696SDavid.Hollister@Sun.COM  *	detach(9E) entrypoint
2685754Sjw149990  */
2695754Sjw149990 static int
smp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2705754Sjw149990 smp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2715754Sjw149990 {
2725754Sjw149990 	int instance;
2735754Sjw149990 	smp_state_t *smp_state;
2745754Sjw149990 
2755754Sjw149990 	instance = ddi_get_instance(dip);
2765754Sjw149990 	smp_state = ddi_get_soft_state(smp_soft_state, instance);
2775754Sjw149990 
2785754Sjw149990 	if (smp_state == NULL) {
2795754Sjw149990 		smp_log(NULL, CE_NOTE,
2805754Sjw149990 		    "!smp_detach: failed, no softstate found (%d), "
2815754Sjw149990 		    "device unit-address @%s",
2825754Sjw149990 		    instance, ddi_get_name_addr(dip));
2835754Sjw149990 		return (DDI_FAILURE);
2845754Sjw149990 	}
2855754Sjw149990 
2865754Sjw149990 	switch (cmd) {
2875754Sjw149990 	case DDI_DETACH:
2885754Sjw149990 		return (smp_do_detach(dip));
2895754Sjw149990 	case DDI_SUSPEND:
2905754Sjw149990 		return (DDI_SUCCESS);
2915754Sjw149990 	default:
2925754Sjw149990 		return (DDI_FAILURE);
2935754Sjw149990 	}
2945754Sjw149990 }
2955754Sjw149990 
2965754Sjw149990 /*
2975754Sjw149990  * smp_do_detach()
29810696SDavid.Hollister@Sun.COM  *	detach the driver, tearing down resources.
2995754Sjw149990  */
3005754Sjw149990 static int
smp_do_detach(dev_info_t * dip)3015754Sjw149990 smp_do_detach(dev_info_t *dip)
3025754Sjw149990 {
3035754Sjw149990 	int instance;
3045754Sjw149990 	smp_state_t *smp_state;
3055754Sjw149990 
3065754Sjw149990 	instance = ddi_get_instance(dip);
3075754Sjw149990 	smp_state = ddi_get_soft_state(smp_soft_state, instance);
3085754Sjw149990 
3095754Sjw149990 	DTRACE_PROBE2(smp__attach__detach, int, instance, char *,
3105754Sjw149990 	    ddi_get_name_addr(dip));
3115754Sjw149990 
3125754Sjw149990 	mutex_destroy(&smp_state->smp_mutex);
3135754Sjw149990 	ddi_soft_state_free(smp_soft_state, instance);
3145754Sjw149990 	ddi_remove_minor_node(dip, NULL);
3155754Sjw149990 	return (DDI_SUCCESS);
3165754Sjw149990 }
3175754Sjw149990 
3185754Sjw149990 /*ARGSUSED*/
3195754Sjw149990 static int
smp_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)3205754Sjw149990 smp_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
3215754Sjw149990 {
3225754Sjw149990 	smp_state_t *smp_state;
3235754Sjw149990 	int instance;
3245754Sjw149990 	int rv = 0;
3255754Sjw149990 
3265754Sjw149990 	instance = getminor(*dev_p);
3275754Sjw149990 	if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
3285754Sjw149990 	    == NULL) {
3295754Sjw149990 		return (ENXIO);
3305754Sjw149990 	}
3315754Sjw149990 
3325754Sjw149990 	mutex_enter(&smp_state->smp_mutex);
3335754Sjw149990 	if (flag & FEXCL) {
3345754Sjw149990 		if (smp_state->smp_open_flag != SMP_CLOSED) {
3355754Sjw149990 			rv = EBUSY;
3365754Sjw149990 		} else {
3375754Sjw149990 			smp_state->smp_open_flag = SMP_EXOPENED;
3385754Sjw149990 		}
3395754Sjw149990 	} else {
3405754Sjw149990 		if (smp_state->smp_open_flag == SMP_EXOPENED) {
3415754Sjw149990 			rv = EBUSY;
3425754Sjw149990 		} else {
3435754Sjw149990 			smp_state->smp_open_flag = SMP_SOPENED;
3445754Sjw149990 		}
3455754Sjw149990 	}
3465754Sjw149990 	mutex_exit(&smp_state->smp_mutex);
3475754Sjw149990 
3485754Sjw149990 	return (rv);
3495754Sjw149990 }
3505754Sjw149990 
3515754Sjw149990 /*ARGSUSED*/
3525754Sjw149990 static int
smp_close(dev_t dev,int flag,int otyp,cred_t * cred_p)3535754Sjw149990 smp_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
3545754Sjw149990 {
3555754Sjw149990 	smp_state_t *smp_state;
3565754Sjw149990 	int instance;
3575754Sjw149990 	int rv = 0;
3585754Sjw149990 
3595754Sjw149990 	instance = getminor(dev);
3605754Sjw149990 	if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
3615754Sjw149990 	    == NULL) {
3625754Sjw149990 		return (ENXIO);
3635754Sjw149990 	}
3645754Sjw149990 
3655754Sjw149990 	mutex_enter(&smp_state->smp_mutex);
3665805Sml198626 	if (smp_state->smp_open_flag == SMP_CLOSED) {
3675805Sml198626 		smp_log(smp_state, CE_NOTE, "!smp device is already in close");
3685805Sml198626 	} else {
3695754Sjw149990 		smp_state->smp_open_flag = SMP_CLOSED;
3705754Sjw149990 	}
3715754Sjw149990 	mutex_exit(&smp_state->smp_mutex);
3725754Sjw149990 	return (rv);
3735754Sjw149990 }
3745754Sjw149990 
3755754Sjw149990 /*ARGSUSED*/
3765754Sjw149990 static int
smp_handle_func(dev_t dev,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)3775754Sjw149990 smp_handle_func(dev_t dev,
3785754Sjw149990     intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
3795754Sjw149990 {
3805754Sjw149990 	usmp_cmd_t usmp_cmd_data, *usmp_cmd = &usmp_cmd_data;
3815754Sjw149990 	smp_pkt_t smp_pkt_data, *smp_pkt = &smp_pkt_data;
3825754Sjw149990 	smp_state_t *smp_state;
3835754Sjw149990 	int instance, retrycount;
3845754Sjw149990 	cred_t *cr;
3855754Sjw149990 	uint64_t cmd_flags = 0;
3865754Sjw149990 	int rval = 0;
3875754Sjw149990 
3885754Sjw149990 #ifdef	_MULTI_DATAMODEL
3895754Sjw149990 	usmp_cmd32_t usmp_cmd32_data, *usmp_cmd32 = &usmp_cmd32_data;
3905754Sjw149990 #endif
3915754Sjw149990 
3925754Sjw149990 	/* require PRIV_SYS_DEVICES privilege */
3935754Sjw149990 	cr = ddi_get_cred();
3945754Sjw149990 	if ((drv_priv(cred_p) != 0) && (drv_priv(cr) != 0)) {
3955754Sjw149990 		return (EPERM);
3965754Sjw149990 	}
3975754Sjw149990 
3985754Sjw149990 	bzero(smp_pkt, sizeof (smp_pkt_t));
3995754Sjw149990 
4005754Sjw149990 	instance = getminor(dev);
4015754Sjw149990 	if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
4025754Sjw149990 	    == NULL) {
4035754Sjw149990 		return (ENXIO);
4045754Sjw149990 	}
4055754Sjw149990 
4065754Sjw149990 #ifdef	_MULTI_DATAMODEL
4075754Sjw149990 	switch (ddi_model_convert_from(flag & FMODELS)) {
4085754Sjw149990 	case DDI_MODEL_ILP32:
4095754Sjw149990 		if (ddi_copyin((void *)arg, usmp_cmd32, sizeof (usmp_cmd32_t),
4105754Sjw149990 		    flag)) {
4115754Sjw149990 			return (EFAULT);
4125754Sjw149990 		}
4135754Sjw149990 
4145754Sjw149990 		usmp_cmd32tousmp_cmd(usmp_cmd32, usmp_cmd);
4155754Sjw149990 		break;
4165754Sjw149990 	case DDI_MODEL_NONE:
4175754Sjw149990 		if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t),
4185754Sjw149990 		    flag)) {
4195754Sjw149990 			return (EFAULT);
4205754Sjw149990 		}
4215754Sjw149990 		break;
4225754Sjw149990 	}
4235754Sjw149990 #else  /* ! _MULTI_DATAMODEL */
4245754Sjw149990 	if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), flag)) {
4255754Sjw149990 		return (EFAULT);
4265754Sjw149990 	}
4275754Sjw149990 #endif	/* _MULTI_DATAMODEL */
4285754Sjw149990 
4295754Sjw149990 	if ((usmp_cmd->usmp_reqsize < SMP_MIN_REQUEST_SIZE) ||
4305754Sjw149990 	    (usmp_cmd->usmp_reqsize > SMP_MAX_REQUEST_SIZE) ||
4315754Sjw149990 	    (usmp_cmd->usmp_rspsize < SMP_MIN_RESPONSE_SIZE) ||
4325754Sjw149990 	    (usmp_cmd->usmp_rspsize > SMP_MAX_RESPONSE_SIZE)) {
4335754Sjw149990 		rval = EINVAL;
4345754Sjw149990 		goto done;
4355754Sjw149990 	}
4365754Sjw149990 
437*11052SChris.Horne@Sun.COM 	smp_pkt->smp_pkt_reqsize = usmp_cmd->usmp_reqsize;
438*11052SChris.Horne@Sun.COM 	smp_pkt->smp_pkt_rspsize = usmp_cmd->usmp_rspsize;
4395754Sjw149990 
4405754Sjw149990 	/* allocate memory space for smp request and response frame in kernel */
441*11052SChris.Horne@Sun.COM 	smp_pkt->smp_pkt_req = kmem_zalloc((size_t)usmp_cmd->usmp_reqsize,
4425754Sjw149990 	    KM_SLEEP);
4435754Sjw149990 	cmd_flags |= SMP_FLAG_REQBUF;
4445754Sjw149990 
445*11052SChris.Horne@Sun.COM 	smp_pkt->smp_pkt_rsp = kmem_zalloc((size_t)usmp_cmd->usmp_rspsize,
4465754Sjw149990 	    KM_SLEEP);
4475754Sjw149990 	cmd_flags |= SMP_FLAG_RSPBUF;
4485754Sjw149990 
4495754Sjw149990 	/* copy smp request frame to kernel space */
450*11052SChris.Horne@Sun.COM 	if (ddi_copyin(usmp_cmd->usmp_req, smp_pkt->smp_pkt_req,
4515754Sjw149990 	    (size_t)usmp_cmd->usmp_reqsize, flag) != 0) {
4525754Sjw149990 		rval = EFAULT;
4535754Sjw149990 		goto done;
4545754Sjw149990 	}
4555754Sjw149990 
456*11052SChris.Horne@Sun.COM 	DTRACE_PROBE1(smp__transport__start, caddr_t, smp_pkt->smp_pkt_req);
4575754Sjw149990 
458*11052SChris.Horne@Sun.COM 	smp_pkt->smp_pkt_address = &smp_state->smp_sd->smp_sd_address;
4595754Sjw149990 	if (usmp_cmd->usmp_timeout <= 0) {
460*11052SChris.Horne@Sun.COM 		smp_pkt->smp_pkt_timeout = SMP_DEFAULT_TIMEOUT;
4615754Sjw149990 	} else {
462*11052SChris.Horne@Sun.COM 		smp_pkt->smp_pkt_timeout = usmp_cmd->usmp_timeout;
4635754Sjw149990 	}
4645754Sjw149990 
465*11052SChris.Horne@Sun.COM 	/* call smp_transport entry and send smp_pkt to HBA driver */
4665754Sjw149990 	cmd_flags |= SMP_FLAG_XFER;
4675754Sjw149990 	for (retrycount = 0; retrycount <= smp_retry_times; retrycount++) {
46810696SDavid.Hollister@Sun.COM 
46910696SDavid.Hollister@Sun.COM 		/*
47010696SDavid.Hollister@Sun.COM 		 * To improve transport reliability, only allow one command
471*11052SChris.Horne@Sun.COM 		 * outstanding at a time in smp_transport().
47210696SDavid.Hollister@Sun.COM 		 *
47310696SDavid.Hollister@Sun.COM 		 * NOTE: Some expanders have issues with heavy smp load.
47410696SDavid.Hollister@Sun.COM 		 */
47510696SDavid.Hollister@Sun.COM 		if (smp_single_command) {
47610696SDavid.Hollister@Sun.COM 			mutex_enter(&smp_state->smp_mutex);
47710696SDavid.Hollister@Sun.COM 			while (smp_state->smp_busy)
47810696SDavid.Hollister@Sun.COM 				cv_wait(&smp_state->smp_cv,
47910696SDavid.Hollister@Sun.COM 				    &smp_state->smp_mutex);
48010696SDavid.Hollister@Sun.COM 			smp_state->smp_busy = 1;
48110696SDavid.Hollister@Sun.COM 			mutex_exit(&smp_state->smp_mutex);
48210696SDavid.Hollister@Sun.COM 		}
48310696SDavid.Hollister@Sun.COM 
48410696SDavid.Hollister@Sun.COM 		/* Let the transport know if more retries are possible. */
485*11052SChris.Horne@Sun.COM 		smp_pkt->smp_pkt_will_retry =
48610696SDavid.Hollister@Sun.COM 		    (retrycount < smp_retry_times) ? 1 : 0;
48710696SDavid.Hollister@Sun.COM 
488*11052SChris.Horne@Sun.COM 		smp_pkt->smp_pkt_reason = 0;
489*11052SChris.Horne@Sun.COM 		rval = smp_transport(smp_pkt);	/* put on the wire */
49010696SDavid.Hollister@Sun.COM 
49110696SDavid.Hollister@Sun.COM 		if (smp_delay_cmd)
49210696SDavid.Hollister@Sun.COM 			delay(drv_usectohz(smp_delay_cmd));
49310696SDavid.Hollister@Sun.COM 
49410696SDavid.Hollister@Sun.COM 		if (smp_single_command) {
49510696SDavid.Hollister@Sun.COM 			mutex_enter(&smp_state->smp_mutex);
49610696SDavid.Hollister@Sun.COM 			smp_state->smp_busy = 0;
49710696SDavid.Hollister@Sun.COM 			cv_signal(&smp_state->smp_cv);
49810696SDavid.Hollister@Sun.COM 			mutex_exit(&smp_state->smp_mutex);
49910696SDavid.Hollister@Sun.COM 		}
50010696SDavid.Hollister@Sun.COM 
50110696SDavid.Hollister@Sun.COM 		if (rval == DDI_SUCCESS) {
50210696SDavid.Hollister@Sun.COM 			if (retrycount)
50310696SDavid.Hollister@Sun.COM 				smp_retry_recovered++;
50410696SDavid.Hollister@Sun.COM 			rval = 0;
5055754Sjw149990 			break;
5065754Sjw149990 		}
5075754Sjw149990 
508*11052SChris.Horne@Sun.COM 		switch (smp_pkt->smp_pkt_reason) {
5095754Sjw149990 		case EAGAIN:
5105754Sjw149990 			if (retrycount < smp_retry_times) {
511*11052SChris.Horne@Sun.COM 				bzero(smp_pkt->smp_pkt_rsp,
5125754Sjw149990 				    (size_t)usmp_cmd->usmp_rspsize);
51310696SDavid.Hollister@Sun.COM 				if (smp_retry_delay)
51410696SDavid.Hollister@Sun.COM 					delay(drv_usectohz(smp_retry_delay));
5155754Sjw149990 				continue;
5165754Sjw149990 			} else {
51710696SDavid.Hollister@Sun.COM 				smp_retry_failed++;
5185754Sjw149990 				smp_log(smp_state, CE_NOTE,
519*11052SChris.Horne@Sun.COM 				    "!smp_transport failed, smp_pkt_reason %d",
520*11052SChris.Horne@Sun.COM 				    smp_pkt->smp_pkt_reason);
521*11052SChris.Horne@Sun.COM 				rval = smp_pkt->smp_pkt_reason;
5225754Sjw149990 				goto copyout;
5235754Sjw149990 			}
5245754Sjw149990 		default:
5255754Sjw149990 			smp_log(smp_state, CE_NOTE,
526*11052SChris.Horne@Sun.COM 			    "!smp_transport failed, smp_pkt_reason %d",
527*11052SChris.Horne@Sun.COM 			    smp_pkt->smp_pkt_reason);
528*11052SChris.Horne@Sun.COM 			rval = smp_pkt->smp_pkt_reason;
5295754Sjw149990 			goto copyout;
5305754Sjw149990 		}
5315754Sjw149990 	}
5325754Sjw149990 
5335754Sjw149990 copyout:
5345754Sjw149990 	/* copy out smp response to user process */
535*11052SChris.Horne@Sun.COM 	if (ddi_copyout(smp_pkt->smp_pkt_rsp, usmp_cmd->usmp_rsp,
5365754Sjw149990 	    (size_t)usmp_cmd->usmp_rspsize, flag) != 0) {
5375754Sjw149990 		rval = EFAULT;
5385754Sjw149990 	}
5395754Sjw149990 
5405754Sjw149990 done:
5415754Sjw149990 	if ((cmd_flags & SMP_FLAG_XFER) != 0) {
542*11052SChris.Horne@Sun.COM 		DTRACE_PROBE2(smp__transport__done, caddr_t,
543*11052SChris.Horne@Sun.COM 		    smp_pkt->smp_pkt_rsp, uchar_t, smp_pkt->smp_pkt_reason);
5445754Sjw149990 	}
5455754Sjw149990 	if ((cmd_flags & SMP_FLAG_REQBUF) != 0) {
546*11052SChris.Horne@Sun.COM 		kmem_free(smp_pkt->smp_pkt_req, smp_pkt->smp_pkt_reqsize);
5475754Sjw149990 	}
5485754Sjw149990 	if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) {
549*11052SChris.Horne@Sun.COM 		kmem_free(smp_pkt->smp_pkt_rsp, smp_pkt->smp_pkt_rspsize);
5505754Sjw149990 	}
55110696SDavid.Hollister@Sun.COM 
55210696SDavid.Hollister@Sun.COM 	if (rval)
55310696SDavid.Hollister@Sun.COM 		smp_failed++;
5545754Sjw149990 	return (rval);
5555754Sjw149990 }
5565754Sjw149990 
5575754Sjw149990 /*ARGSUSED*/
5585754Sjw149990 static int
smp_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)5595754Sjw149990 smp_ioctl(dev_t dev,
5605754Sjw149990     int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
5615754Sjw149990 {
5625754Sjw149990 	int rval = 0;
5635754Sjw149990 
5645754Sjw149990 	switch (cmd) {
5655754Sjw149990 	case USMPFUNC:
5665754Sjw149990 		/*
5675754Sjw149990 		 * The response payload is valid only if return value is 0
5685754Sjw149990 		 * or EOVERFLOW.
5695754Sjw149990 		 */
5705754Sjw149990 		rval = smp_handle_func(dev, arg, flag, cred_p, rval_p);
5715754Sjw149990 		break;
5725754Sjw149990 	default:
5735754Sjw149990 		rval = EINVAL;
5745754Sjw149990 	}
5755754Sjw149990 	return (rval);
5765754Sjw149990 }
5775754Sjw149990 
5785754Sjw149990 static void
smp_log(smp_state_t * smp_state,int level,const char * fmt,...)5795754Sjw149990 smp_log(smp_state_t *smp_state, int level, const char *fmt, ...)
5805754Sjw149990 {
5815754Sjw149990 	va_list	ap;
5825754Sjw149990 	char buf[256];
5835754Sjw149990 	dev_info_t *dip;
5845754Sjw149990 
5855754Sjw149990 	if (smp_state == (smp_state_t *)NULL) {
5865754Sjw149990 		dip = NULL;
5875754Sjw149990 	} else {
588*11052SChris.Horne@Sun.COM 		dip = smp_state->smp_sd->smp_sd_dev;
5895754Sjw149990 	}
5905754Sjw149990 
5915754Sjw149990 	va_start(ap, fmt);
5925754Sjw149990 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
5935754Sjw149990 	va_end(ap);
5945754Sjw149990 
5955754Sjw149990 	scsi_log(dip, "smp", level, "%s", buf);
5965754Sjw149990 }
597