xref: /titanic_51/usr/src/uts/common/io/scsi/targets/smp.c (revision 96c4a178a18cd52ee5001195f1552d9cef0c38f0)
1936b7af6Sjw149990 /*
2936b7af6Sjw149990  * CDDL HEADER START
3936b7af6Sjw149990  *
4936b7af6Sjw149990  * The contents of this file are subject to the terms of the
5936b7af6Sjw149990  * Common Development and Distribution License (the "License").
6936b7af6Sjw149990  * You may not use this file except in compliance with the License.
7936b7af6Sjw149990  *
8936b7af6Sjw149990  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9936b7af6Sjw149990  * or http://www.opensolaris.org/os/licensing.
10936b7af6Sjw149990  * See the License for the specific language governing permissions
11936b7af6Sjw149990  * and limitations under the License.
12936b7af6Sjw149990  *
13936b7af6Sjw149990  * When distributing Covered Code, include this CDDL HEADER in each
14936b7af6Sjw149990  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15936b7af6Sjw149990  * If applicable, add the following below this CDDL HEADER, with the
16936b7af6Sjw149990  * fields enclosed by brackets "[]" replaced with your own identifying
17936b7af6Sjw149990  * information: Portions Copyright [yyyy] [name of copyright owner]
18936b7af6Sjw149990  *
19936b7af6Sjw149990  * CDDL HEADER END
20936b7af6Sjw149990  */
21936b7af6Sjw149990 
22936b7af6Sjw149990 /*
234c06356bSdh142964  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24936b7af6Sjw149990  * Use is subject to license terms.
25936b7af6Sjw149990  */
26936b7af6Sjw149990 
27936b7af6Sjw149990 
28936b7af6Sjw149990 /*
29936b7af6Sjw149990  * SMP - Serial Management Protocol Device Driver
30936b7af6Sjw149990  *
31936b7af6Sjw149990  * The SMP driver provides user programs access to SAS Serial Management
32936b7af6Sjw149990  * Protocol devices by providing ioctl interface.
33936b7af6Sjw149990  */
34936b7af6Sjw149990 
35936b7af6Sjw149990 #include <sys/modctl.h>
36936b7af6Sjw149990 #include <sys/file.h>
37936b7af6Sjw149990 #include <sys/scsi/scsi.h>
38936b7af6Sjw149990 #include <sys/scsi/targets/smp.h>
39936b7af6Sjw149990 #include <sys/sdt.h>
40936b7af6Sjw149990 
41936b7af6Sjw149990 /*
42936b7af6Sjw149990  * Standard entrypoints
43936b7af6Sjw149990  */
44936b7af6Sjw149990 static int smp_attach(dev_info_t *, ddi_attach_cmd_t);
45936b7af6Sjw149990 static int smp_detach(dev_info_t *, ddi_detach_cmd_t);
46936b7af6Sjw149990 static int smp_open(dev_t *, int, int, cred_t *);
47936b7af6Sjw149990 static int smp_close(dev_t, int, int, cred_t *);
48936b7af6Sjw149990 static int smp_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
49936b7af6Sjw149990 
50936b7af6Sjw149990 /*
51936b7af6Sjw149990  * Configuration routines
52936b7af6Sjw149990  */
53936b7af6Sjw149990 static int smp_do_attach(dev_info_t *);
54936b7af6Sjw149990 static int smp_do_detach(dev_info_t *);
55936b7af6Sjw149990 
56936b7af6Sjw149990 /*
57936b7af6Sjw149990  * Command handle routing
58936b7af6Sjw149990  */
59936b7af6Sjw149990 static int smp_handle_func(dev_t, intptr_t, int, cred_t *, int *);
60936b7af6Sjw149990 
61936b7af6Sjw149990 /*
62936b7af6Sjw149990  * Logging/debugging routines
63936b7af6Sjw149990  */
64936b7af6Sjw149990 static void smp_log(smp_state_t  *, int,  const char *, ...);
65936b7af6Sjw149990 
66936b7af6Sjw149990 int smp_retry_times	= SMP_DEFAULT_RETRY_TIMES;
674c06356bSdh142964 int smp_retry_delay	= 10000;	/* 10msec */
684c06356bSdh142964 int smp_delay_cmd	= 1;		/* 1usec */
694c06356bSdh142964 int smp_single_command	= 1;		/* one command at a time */
704c06356bSdh142964 
714c06356bSdh142964 static int smp_retry_recovered	= 0;	/* retry recovery counter */
724c06356bSdh142964 static int smp_retry_failed	= 0;	/* retry failed counter */
734c06356bSdh142964 static int smp_failed		= 0;
74936b7af6Sjw149990 
75936b7af6Sjw149990 static struct cb_ops smp_cb_ops = {
76936b7af6Sjw149990 	smp_open,			/* open */
77936b7af6Sjw149990 	smp_close,			/* close */
78936b7af6Sjw149990 	nodev,				/* strategy */
79936b7af6Sjw149990 	nodev,				/* print */
80936b7af6Sjw149990 	nodev,				/* dump */
81936b7af6Sjw149990 	nodev,				/* read */
82936b7af6Sjw149990 	nodev,				/* write */
83936b7af6Sjw149990 	smp_ioctl,			/* ioctl */
84936b7af6Sjw149990 	nodev,				/* devmap */
85936b7af6Sjw149990 	nodev,				/* mmap */
86936b7af6Sjw149990 	nodev,				/* segmap */
87936b7af6Sjw149990 	nochpoll,			/* poll */
88936b7af6Sjw149990 	ddi_prop_op,			/* cb_prop_op */
89936b7af6Sjw149990 	0,				/* streamtab  */
90936b7af6Sjw149990 	D_MP | D_NEW | D_HOTPLUG	/* Driver compatibility flag */
91936b7af6Sjw149990 };
92936b7af6Sjw149990 
93936b7af6Sjw149990 static struct dev_ops smp_dev_ops = {
94936b7af6Sjw149990 	DEVO_REV,		/* devo_rev, */
95936b7af6Sjw149990 	0,			/* refcnt  */
96*96c4a178SChris Horne 	ddi_getinfo_1to1,	/* info */
97936b7af6Sjw149990 	nulldev,		/* identify */
98*96c4a178SChris Horne 	NULL,			/* probe */
99936b7af6Sjw149990 	smp_attach,		/* attach */
100936b7af6Sjw149990 	smp_detach,		/* detach */
101936b7af6Sjw149990 	nodev,			/* reset */
102936b7af6Sjw149990 	&smp_cb_ops,		/* driver operations */
103936b7af6Sjw149990 	(struct bus_ops *)0,	/* bus operations */
10419397407SSherry Moore 	NULL,			/* power */
10519397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
106936b7af6Sjw149990 };
107936b7af6Sjw149990 
108936b7af6Sjw149990 static void *smp_soft_state = NULL;
109936b7af6Sjw149990 
110936b7af6Sjw149990 static struct modldrv modldrv = {
11119397407SSherry Moore 	&mod_driverops, "smp device driver", &smp_dev_ops
112936b7af6Sjw149990 };
113936b7af6Sjw149990 
114936b7af6Sjw149990 static struct modlinkage modlinkage = {
115936b7af6Sjw149990 	MODREV_1, &modldrv, NULL
116936b7af6Sjw149990 };
117936b7af6Sjw149990 
118936b7af6Sjw149990 int
119936b7af6Sjw149990 _init(void)
120936b7af6Sjw149990 {
121936b7af6Sjw149990 	int err;
122936b7af6Sjw149990 
123936b7af6Sjw149990 	if ((err = ddi_soft_state_init(&smp_soft_state,
124936b7af6Sjw149990 	    sizeof (smp_state_t), SMP_ESTIMATED_NUM_DEVS)) != 0) {
125936b7af6Sjw149990 		return (err);
126936b7af6Sjw149990 	}
127936b7af6Sjw149990 
128936b7af6Sjw149990 	if ((err = mod_install(&modlinkage)) != 0) {
129936b7af6Sjw149990 		ddi_soft_state_fini(&smp_soft_state);
130936b7af6Sjw149990 	}
131936b7af6Sjw149990 
132936b7af6Sjw149990 	return (err);
133936b7af6Sjw149990 }
134936b7af6Sjw149990 
135936b7af6Sjw149990 int
136936b7af6Sjw149990 _fini(void)
137936b7af6Sjw149990 {
138936b7af6Sjw149990 	int err;
139936b7af6Sjw149990 
140936b7af6Sjw149990 	if ((err = mod_remove(&modlinkage)) == 0) {
141936b7af6Sjw149990 		ddi_soft_state_fini(&smp_soft_state);
142936b7af6Sjw149990 	}
143936b7af6Sjw149990 
144936b7af6Sjw149990 	return (err);
145936b7af6Sjw149990 }
146936b7af6Sjw149990 
147936b7af6Sjw149990 int
148936b7af6Sjw149990 _info(struct modinfo *modinfop)
149936b7af6Sjw149990 {
150936b7af6Sjw149990 	return (mod_info(&modlinkage, modinfop));
151936b7af6Sjw149990 }
152936b7af6Sjw149990 
153936b7af6Sjw149990 /*
154936b7af6Sjw149990  * smp_attach()
155936b7af6Sjw149990  *	attach(9e) entrypoint.
156936b7af6Sjw149990  */
157936b7af6Sjw149990 static int
158936b7af6Sjw149990 smp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
159936b7af6Sjw149990 {
160936b7af6Sjw149990 	int err;
161936b7af6Sjw149990 
162936b7af6Sjw149990 	switch (cmd) {
163936b7af6Sjw149990 	case DDI_ATTACH:
164936b7af6Sjw149990 		err = smp_do_attach(dip);
165936b7af6Sjw149990 		break;
166936b7af6Sjw149990 	case DDI_RESUME:
167936b7af6Sjw149990 		err = DDI_SUCCESS;
168936b7af6Sjw149990 		break;
169936b7af6Sjw149990 	default:
170936b7af6Sjw149990 		err = DDI_FAILURE;
171936b7af6Sjw149990 		break;
172936b7af6Sjw149990 	}
173936b7af6Sjw149990 
174936b7af6Sjw149990 	if (err != DDI_SUCCESS) {
175936b7af6Sjw149990 		smp_log(NULL, CE_NOTE, "!smp_attach(), "
176936b7af6Sjw149990 		    "device unit-address @%s failed",
177936b7af6Sjw149990 		    ddi_get_name_addr(dip));
178936b7af6Sjw149990 	}
179936b7af6Sjw149990 	return (err);
180936b7af6Sjw149990 }
181936b7af6Sjw149990 
182936b7af6Sjw149990 /*
183936b7af6Sjw149990  * smp_do_attach()
184936b7af6Sjw149990  *	handle the nitty details of attach.
185936b7af6Sjw149990  */
186936b7af6Sjw149990 static int
187936b7af6Sjw149990 smp_do_attach(dev_info_t *dip)
188936b7af6Sjw149990 {
189936b7af6Sjw149990 	int			instance;
190*96c4a178SChris Horne 	struct smp_device	*smp_sd;
191*96c4a178SChris Horne 	uchar_t			*srmir = NULL;
192*96c4a178SChris Horne 	uint_t			srmirlen = 0;
193*96c4a178SChris Horne 	ddi_devid_t		devid = NULL;
194936b7af6Sjw149990 	smp_state_t		*smp_state;
195936b7af6Sjw149990 
196936b7af6Sjw149990 	instance = ddi_get_instance(dip);
197*96c4a178SChris Horne 	smp_sd = ddi_get_driver_private(dip);
198*96c4a178SChris Horne 	ASSERT(smp_sd != NULL);
199936b7af6Sjw149990 
200936b7af6Sjw149990 	DTRACE_PROBE2(smp__attach__detach, int, instance, char *,
201936b7af6Sjw149990 	    ddi_get_name_addr(dip));
202936b7af6Sjw149990 
203*96c4a178SChris Horne 	/* make sure device is there, and establish srmir identity property */
204*96c4a178SChris Horne 	if (smp_probe(smp_sd) != DDI_PROBE_SUCCESS) {
205*96c4a178SChris Horne 		smp_log(NULL, CE_NOTE,
206*96c4a178SChris Horne 		    "!smp_do_attach: failed smp_probe, "
207*96c4a178SChris Horne 		    "device unit-address @%s", ddi_get_name_addr(dip));
208*96c4a178SChris Horne 		return (DDI_FAILURE);
209*96c4a178SChris Horne 	}
210*96c4a178SChris Horne 
211*96c4a178SChris Horne 	/* if we have not already registered a devid, then do so now  */
212*96c4a178SChris Horne 	if (ddi_devid_get(dip, &devid) != DDI_SUCCESS) {
213*96c4a178SChris Horne 		/* get the srmir identity information for use in devid */
214*96c4a178SChris Horne 		(void) ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
215*96c4a178SChris Horne 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
216*96c4a178SChris Horne 		    SMP_PROP_REPORT_MANUFACTURER, &srmir, &srmirlen);
217*96c4a178SChris Horne 
218*96c4a178SChris Horne 		/* Convert smp unit-address and srmir into devid */
219*96c4a178SChris Horne 		if (ddi_devid_smp_encode(DEVID_SMP_ENCODE_VERSION_LATEST,
220*96c4a178SChris Horne 		    (char *)ddi_driver_name(dip), ddi_get_name_addr(dip),
221*96c4a178SChris Horne 		    srmir, srmirlen, &devid) == DDI_SUCCESS) {
222*96c4a178SChris Horne 			/* register the devid */
223*96c4a178SChris Horne 			(void) ddi_devid_register(dip, devid);
224*96c4a178SChris Horne 		}
225*96c4a178SChris Horne 		ddi_prop_free(srmir);
226*96c4a178SChris Horne 	}
227*96c4a178SChris Horne 
228*96c4a178SChris Horne 	/* We don't need the devid for our own operation, so free now. */
229*96c4a178SChris Horne 	if (devid)
230*96c4a178SChris Horne 		ddi_devid_free(devid);
231*96c4a178SChris Horne 
232*96c4a178SChris Horne 	/* we are now done with srmir identity property defined by smp_probe */
233*96c4a178SChris Horne 	(void) ndi_prop_remove(DDI_DEV_T_NONE,
234*96c4a178SChris Horne 	    dip, SMP_PROP_REPORT_MANUFACTURER);
235*96c4a178SChris Horne 
236936b7af6Sjw149990 	if (ddi_soft_state_zalloc(smp_soft_state, instance) != DDI_SUCCESS) {
237936b7af6Sjw149990 		smp_log(NULL, CE_NOTE,
238936b7af6Sjw149990 		    "!smp_do_attach: failed to allocate softstate, "
239936b7af6Sjw149990 		    "device unit-address @%s", ddi_get_name_addr(dip));
240936b7af6Sjw149990 		return (DDI_FAILURE);
241936b7af6Sjw149990 	}
242936b7af6Sjw149990 
243936b7af6Sjw149990 	smp_state = ddi_get_soft_state(smp_soft_state, instance);
244*96c4a178SChris Horne 	smp_state->smp_sd = smp_sd;
245936b7af6Sjw149990 
246936b7af6Sjw149990 	/*
247936b7af6Sjw149990 	 * For simplicity, the minor number == the instance number
248936b7af6Sjw149990 	 */
249936b7af6Sjw149990 	if (ddi_create_minor_node(dip, "smp", S_IFCHR,
250936b7af6Sjw149990 	    instance, DDI_NT_SMP, NULL) == DDI_FAILURE) {
251936b7af6Sjw149990 		smp_log(smp_state, CE_NOTE,
252936b7af6Sjw149990 		    "!smp_do_attach: minor node creation failed, "
253936b7af6Sjw149990 		    "device unit-address @%s", ddi_get_name_addr(dip));
254936b7af6Sjw149990 		ddi_soft_state_free(smp_soft_state, instance);
255936b7af6Sjw149990 		return (DDI_FAILURE);
256936b7af6Sjw149990 	}
257936b7af6Sjw149990 
258936b7af6Sjw149990 	mutex_init(&smp_state->smp_mutex, NULL, MUTEX_DRIVER, NULL);
259936b7af6Sjw149990 	smp_state->smp_open_flag = SMP_CLOSED;
260936b7af6Sjw149990 
261936b7af6Sjw149990 	ddi_report_dev(dip);
262936b7af6Sjw149990 	return (DDI_SUCCESS);
263936b7af6Sjw149990 }
264936b7af6Sjw149990 
265936b7af6Sjw149990 /*
266936b7af6Sjw149990  * smp_detach()
267936b7af6Sjw149990  *	detach(9E) entrypoint
268936b7af6Sjw149990  */
269936b7af6Sjw149990 static int
270936b7af6Sjw149990 smp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
271936b7af6Sjw149990 {
272936b7af6Sjw149990 	int instance;
273936b7af6Sjw149990 	smp_state_t *smp_state;
274936b7af6Sjw149990 
275936b7af6Sjw149990 	instance = ddi_get_instance(dip);
276936b7af6Sjw149990 	smp_state = ddi_get_soft_state(smp_soft_state, instance);
277936b7af6Sjw149990 
278936b7af6Sjw149990 	if (smp_state == NULL) {
279936b7af6Sjw149990 		smp_log(NULL, CE_NOTE,
280936b7af6Sjw149990 		    "!smp_detach: failed, no softstate found (%d), "
281936b7af6Sjw149990 		    "device unit-address @%s",
282936b7af6Sjw149990 		    instance, ddi_get_name_addr(dip));
283936b7af6Sjw149990 		return (DDI_FAILURE);
284936b7af6Sjw149990 	}
285936b7af6Sjw149990 
286936b7af6Sjw149990 	switch (cmd) {
287936b7af6Sjw149990 	case DDI_DETACH:
288936b7af6Sjw149990 		return (smp_do_detach(dip));
289936b7af6Sjw149990 	case DDI_SUSPEND:
290936b7af6Sjw149990 		return (DDI_SUCCESS);
291936b7af6Sjw149990 	default:
292936b7af6Sjw149990 		return (DDI_FAILURE);
293936b7af6Sjw149990 	}
294936b7af6Sjw149990 }
295936b7af6Sjw149990 
296936b7af6Sjw149990 /*
297936b7af6Sjw149990  * smp_do_detach()
298936b7af6Sjw149990  *	detach the driver, tearing down resources.
299936b7af6Sjw149990  */
300936b7af6Sjw149990 static int
301936b7af6Sjw149990 smp_do_detach(dev_info_t *dip)
302936b7af6Sjw149990 {
303936b7af6Sjw149990 	int instance;
304936b7af6Sjw149990 	smp_state_t *smp_state;
305936b7af6Sjw149990 
306936b7af6Sjw149990 	instance = ddi_get_instance(dip);
307936b7af6Sjw149990 	smp_state = ddi_get_soft_state(smp_soft_state, instance);
308936b7af6Sjw149990 
309936b7af6Sjw149990 	DTRACE_PROBE2(smp__attach__detach, int, instance, char *,
310936b7af6Sjw149990 	    ddi_get_name_addr(dip));
311936b7af6Sjw149990 
312936b7af6Sjw149990 	mutex_destroy(&smp_state->smp_mutex);
313936b7af6Sjw149990 	ddi_soft_state_free(smp_soft_state, instance);
314936b7af6Sjw149990 	ddi_remove_minor_node(dip, NULL);
315936b7af6Sjw149990 	return (DDI_SUCCESS);
316936b7af6Sjw149990 }
317936b7af6Sjw149990 
318936b7af6Sjw149990 /*ARGSUSED*/
319936b7af6Sjw149990 static int
320936b7af6Sjw149990 smp_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
321936b7af6Sjw149990 {
322936b7af6Sjw149990 	smp_state_t *smp_state;
323936b7af6Sjw149990 	int instance;
324936b7af6Sjw149990 	int rv = 0;
325936b7af6Sjw149990 
326936b7af6Sjw149990 	instance = getminor(*dev_p);
327936b7af6Sjw149990 	if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
328936b7af6Sjw149990 	    == NULL) {
329936b7af6Sjw149990 		return (ENXIO);
330936b7af6Sjw149990 	}
331936b7af6Sjw149990 
332936b7af6Sjw149990 	mutex_enter(&smp_state->smp_mutex);
333936b7af6Sjw149990 	if (flag & FEXCL) {
334936b7af6Sjw149990 		if (smp_state->smp_open_flag != SMP_CLOSED) {
335936b7af6Sjw149990 			rv = EBUSY;
336936b7af6Sjw149990 		} else {
337936b7af6Sjw149990 			smp_state->smp_open_flag = SMP_EXOPENED;
338936b7af6Sjw149990 		}
339936b7af6Sjw149990 	} else {
340936b7af6Sjw149990 		if (smp_state->smp_open_flag == SMP_EXOPENED) {
341936b7af6Sjw149990 			rv = EBUSY;
342936b7af6Sjw149990 		} else {
343936b7af6Sjw149990 			smp_state->smp_open_flag = SMP_SOPENED;
344936b7af6Sjw149990 		}
345936b7af6Sjw149990 	}
346936b7af6Sjw149990 	mutex_exit(&smp_state->smp_mutex);
347936b7af6Sjw149990 
348936b7af6Sjw149990 	return (rv);
349936b7af6Sjw149990 }
350936b7af6Sjw149990 
351936b7af6Sjw149990 /*ARGSUSED*/
352936b7af6Sjw149990 static int
353936b7af6Sjw149990 smp_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
354936b7af6Sjw149990 {
355936b7af6Sjw149990 	smp_state_t *smp_state;
356936b7af6Sjw149990 	int instance;
357936b7af6Sjw149990 	int rv = 0;
358936b7af6Sjw149990 
359936b7af6Sjw149990 	instance = getminor(dev);
360936b7af6Sjw149990 	if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
361936b7af6Sjw149990 	    == NULL) {
362936b7af6Sjw149990 		return (ENXIO);
363936b7af6Sjw149990 	}
364936b7af6Sjw149990 
365936b7af6Sjw149990 	mutex_enter(&smp_state->smp_mutex);
3668eadeb34Sml198626 	if (smp_state->smp_open_flag == SMP_CLOSED) {
3678eadeb34Sml198626 		smp_log(smp_state, CE_NOTE, "!smp device is already in close");
3688eadeb34Sml198626 	} else {
369936b7af6Sjw149990 		smp_state->smp_open_flag = SMP_CLOSED;
370936b7af6Sjw149990 	}
371936b7af6Sjw149990 	mutex_exit(&smp_state->smp_mutex);
372936b7af6Sjw149990 	return (rv);
373936b7af6Sjw149990 }
374936b7af6Sjw149990 
375936b7af6Sjw149990 /*ARGSUSED*/
376936b7af6Sjw149990 static int
377936b7af6Sjw149990 smp_handle_func(dev_t dev,
378936b7af6Sjw149990     intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
379936b7af6Sjw149990 {
380936b7af6Sjw149990 	usmp_cmd_t usmp_cmd_data, *usmp_cmd = &usmp_cmd_data;
381936b7af6Sjw149990 	smp_pkt_t smp_pkt_data, *smp_pkt = &smp_pkt_data;
382936b7af6Sjw149990 	smp_state_t *smp_state;
383936b7af6Sjw149990 	int instance, retrycount;
384936b7af6Sjw149990 	cred_t *cr;
385936b7af6Sjw149990 	uint64_t cmd_flags = 0;
386936b7af6Sjw149990 	int rval = 0;
387936b7af6Sjw149990 
388936b7af6Sjw149990 #ifdef	_MULTI_DATAMODEL
389936b7af6Sjw149990 	usmp_cmd32_t usmp_cmd32_data, *usmp_cmd32 = &usmp_cmd32_data;
390936b7af6Sjw149990 #endif
391936b7af6Sjw149990 
392936b7af6Sjw149990 	/* require PRIV_SYS_DEVICES privilege */
393936b7af6Sjw149990 	cr = ddi_get_cred();
394936b7af6Sjw149990 	if ((drv_priv(cred_p) != 0) && (drv_priv(cr) != 0)) {
395936b7af6Sjw149990 		return (EPERM);
396936b7af6Sjw149990 	}
397936b7af6Sjw149990 
398936b7af6Sjw149990 	bzero(smp_pkt, sizeof (smp_pkt_t));
399936b7af6Sjw149990 
400936b7af6Sjw149990 	instance = getminor(dev);
401936b7af6Sjw149990 	if ((smp_state = ddi_get_soft_state(smp_soft_state, instance))
402936b7af6Sjw149990 	    == NULL) {
403936b7af6Sjw149990 		return (ENXIO);
404936b7af6Sjw149990 	}
405936b7af6Sjw149990 
406936b7af6Sjw149990 #ifdef	_MULTI_DATAMODEL
407936b7af6Sjw149990 	switch (ddi_model_convert_from(flag & FMODELS)) {
408936b7af6Sjw149990 	case DDI_MODEL_ILP32:
409936b7af6Sjw149990 		if (ddi_copyin((void *)arg, usmp_cmd32, sizeof (usmp_cmd32_t),
410936b7af6Sjw149990 		    flag)) {
411936b7af6Sjw149990 			return (EFAULT);
412936b7af6Sjw149990 		}
413936b7af6Sjw149990 
414936b7af6Sjw149990 		usmp_cmd32tousmp_cmd(usmp_cmd32, usmp_cmd);
415936b7af6Sjw149990 		break;
416936b7af6Sjw149990 	case DDI_MODEL_NONE:
417936b7af6Sjw149990 		if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t),
418936b7af6Sjw149990 		    flag)) {
419936b7af6Sjw149990 			return (EFAULT);
420936b7af6Sjw149990 		}
421936b7af6Sjw149990 		break;
422936b7af6Sjw149990 	}
423936b7af6Sjw149990 #else  /* ! _MULTI_DATAMODEL */
424936b7af6Sjw149990 	if (ddi_copyin((void *)arg, usmp_cmd, sizeof (usmp_cmd_t), flag)) {
425936b7af6Sjw149990 		return (EFAULT);
426936b7af6Sjw149990 	}
427936b7af6Sjw149990 #endif	/* _MULTI_DATAMODEL */
428936b7af6Sjw149990 
429936b7af6Sjw149990 	if ((usmp_cmd->usmp_reqsize < SMP_MIN_REQUEST_SIZE) ||
430936b7af6Sjw149990 	    (usmp_cmd->usmp_reqsize > SMP_MAX_REQUEST_SIZE) ||
431936b7af6Sjw149990 	    (usmp_cmd->usmp_rspsize < SMP_MIN_RESPONSE_SIZE) ||
432936b7af6Sjw149990 	    (usmp_cmd->usmp_rspsize > SMP_MAX_RESPONSE_SIZE)) {
433936b7af6Sjw149990 		rval = EINVAL;
434936b7af6Sjw149990 		goto done;
435936b7af6Sjw149990 	}
436936b7af6Sjw149990 
437*96c4a178SChris Horne 	smp_pkt->smp_pkt_reqsize = usmp_cmd->usmp_reqsize;
438*96c4a178SChris Horne 	smp_pkt->smp_pkt_rspsize = usmp_cmd->usmp_rspsize;
439936b7af6Sjw149990 
440936b7af6Sjw149990 	/* allocate memory space for smp request and response frame in kernel */
441*96c4a178SChris Horne 	smp_pkt->smp_pkt_req = kmem_zalloc((size_t)usmp_cmd->usmp_reqsize,
442936b7af6Sjw149990 	    KM_SLEEP);
443936b7af6Sjw149990 	cmd_flags |= SMP_FLAG_REQBUF;
444936b7af6Sjw149990 
445*96c4a178SChris Horne 	smp_pkt->smp_pkt_rsp = kmem_zalloc((size_t)usmp_cmd->usmp_rspsize,
446936b7af6Sjw149990 	    KM_SLEEP);
447936b7af6Sjw149990 	cmd_flags |= SMP_FLAG_RSPBUF;
448936b7af6Sjw149990 
449936b7af6Sjw149990 	/* copy smp request frame to kernel space */
450*96c4a178SChris Horne 	if (ddi_copyin(usmp_cmd->usmp_req, smp_pkt->smp_pkt_req,
451936b7af6Sjw149990 	    (size_t)usmp_cmd->usmp_reqsize, flag) != 0) {
452936b7af6Sjw149990 		rval = EFAULT;
453936b7af6Sjw149990 		goto done;
454936b7af6Sjw149990 	}
455936b7af6Sjw149990 
456*96c4a178SChris Horne 	DTRACE_PROBE1(smp__transport__start, caddr_t, smp_pkt->smp_pkt_req);
457936b7af6Sjw149990 
458*96c4a178SChris Horne 	smp_pkt->smp_pkt_address = &smp_state->smp_sd->smp_sd_address;
459936b7af6Sjw149990 	if (usmp_cmd->usmp_timeout <= 0) {
460*96c4a178SChris Horne 		smp_pkt->smp_pkt_timeout = SMP_DEFAULT_TIMEOUT;
461936b7af6Sjw149990 	} else {
462*96c4a178SChris Horne 		smp_pkt->smp_pkt_timeout = usmp_cmd->usmp_timeout;
463936b7af6Sjw149990 	}
464936b7af6Sjw149990 
465*96c4a178SChris Horne 	/* call smp_transport entry and send smp_pkt to HBA driver */
466936b7af6Sjw149990 	cmd_flags |= SMP_FLAG_XFER;
467936b7af6Sjw149990 	for (retrycount = 0; retrycount <= smp_retry_times; retrycount++) {
4684c06356bSdh142964 
4694c06356bSdh142964 		/*
4704c06356bSdh142964 		 * To improve transport reliability, only allow one command
471*96c4a178SChris Horne 		 * outstanding at a time in smp_transport().
4724c06356bSdh142964 		 *
4734c06356bSdh142964 		 * NOTE: Some expanders have issues with heavy smp load.
4744c06356bSdh142964 		 */
4754c06356bSdh142964 		if (smp_single_command) {
4764c06356bSdh142964 			mutex_enter(&smp_state->smp_mutex);
4774c06356bSdh142964 			while (smp_state->smp_busy)
4784c06356bSdh142964 				cv_wait(&smp_state->smp_cv,
4794c06356bSdh142964 				    &smp_state->smp_mutex);
4804c06356bSdh142964 			smp_state->smp_busy = 1;
4814c06356bSdh142964 			mutex_exit(&smp_state->smp_mutex);
4824c06356bSdh142964 		}
4834c06356bSdh142964 
4844c06356bSdh142964 		/* Let the transport know if more retries are possible. */
485*96c4a178SChris Horne 		smp_pkt->smp_pkt_will_retry =
4864c06356bSdh142964 		    (retrycount < smp_retry_times) ? 1 : 0;
4874c06356bSdh142964 
488*96c4a178SChris Horne 		smp_pkt->smp_pkt_reason = 0;
489*96c4a178SChris Horne 		rval = smp_transport(smp_pkt);	/* put on the wire */
4904c06356bSdh142964 
4914c06356bSdh142964 		if (smp_delay_cmd)
4924c06356bSdh142964 			delay(drv_usectohz(smp_delay_cmd));
4934c06356bSdh142964 
4944c06356bSdh142964 		if (smp_single_command) {
4954c06356bSdh142964 			mutex_enter(&smp_state->smp_mutex);
4964c06356bSdh142964 			smp_state->smp_busy = 0;
4974c06356bSdh142964 			cv_signal(&smp_state->smp_cv);
4984c06356bSdh142964 			mutex_exit(&smp_state->smp_mutex);
4994c06356bSdh142964 		}
5004c06356bSdh142964 
5014c06356bSdh142964 		if (rval == DDI_SUCCESS) {
5024c06356bSdh142964 			if (retrycount)
5034c06356bSdh142964 				smp_retry_recovered++;
5044c06356bSdh142964 			rval = 0;
505936b7af6Sjw149990 			break;
506936b7af6Sjw149990 		}
507936b7af6Sjw149990 
508*96c4a178SChris Horne 		switch (smp_pkt->smp_pkt_reason) {
509936b7af6Sjw149990 		case EAGAIN:
510936b7af6Sjw149990 			if (retrycount < smp_retry_times) {
511*96c4a178SChris Horne 				bzero(smp_pkt->smp_pkt_rsp,
512936b7af6Sjw149990 				    (size_t)usmp_cmd->usmp_rspsize);
5134c06356bSdh142964 				if (smp_retry_delay)
5144c06356bSdh142964 					delay(drv_usectohz(smp_retry_delay));
515936b7af6Sjw149990 				continue;
516936b7af6Sjw149990 			} else {
5174c06356bSdh142964 				smp_retry_failed++;
518936b7af6Sjw149990 				smp_log(smp_state, CE_NOTE,
519*96c4a178SChris Horne 				    "!smp_transport failed, smp_pkt_reason %d",
520*96c4a178SChris Horne 				    smp_pkt->smp_pkt_reason);
521*96c4a178SChris Horne 				rval = smp_pkt->smp_pkt_reason;
522936b7af6Sjw149990 				goto copyout;
523936b7af6Sjw149990 			}
524936b7af6Sjw149990 		default:
525936b7af6Sjw149990 			smp_log(smp_state, CE_NOTE,
526*96c4a178SChris Horne 			    "!smp_transport failed, smp_pkt_reason %d",
527*96c4a178SChris Horne 			    smp_pkt->smp_pkt_reason);
528*96c4a178SChris Horne 			rval = smp_pkt->smp_pkt_reason;
529936b7af6Sjw149990 			goto copyout;
530936b7af6Sjw149990 		}
531936b7af6Sjw149990 	}
532936b7af6Sjw149990 
533936b7af6Sjw149990 copyout:
534936b7af6Sjw149990 	/* copy out smp response to user process */
535*96c4a178SChris Horne 	if (ddi_copyout(smp_pkt->smp_pkt_rsp, usmp_cmd->usmp_rsp,
536936b7af6Sjw149990 	    (size_t)usmp_cmd->usmp_rspsize, flag) != 0) {
537936b7af6Sjw149990 		rval = EFAULT;
538936b7af6Sjw149990 	}
539936b7af6Sjw149990 
540936b7af6Sjw149990 done:
541936b7af6Sjw149990 	if ((cmd_flags & SMP_FLAG_XFER) != 0) {
542*96c4a178SChris Horne 		DTRACE_PROBE2(smp__transport__done, caddr_t,
543*96c4a178SChris Horne 		    smp_pkt->smp_pkt_rsp, uchar_t, smp_pkt->smp_pkt_reason);
544936b7af6Sjw149990 	}
545936b7af6Sjw149990 	if ((cmd_flags & SMP_FLAG_REQBUF) != 0) {
546*96c4a178SChris Horne 		kmem_free(smp_pkt->smp_pkt_req, smp_pkt->smp_pkt_reqsize);
547936b7af6Sjw149990 	}
548936b7af6Sjw149990 	if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) {
549*96c4a178SChris Horne 		kmem_free(smp_pkt->smp_pkt_rsp, smp_pkt->smp_pkt_rspsize);
550936b7af6Sjw149990 	}
5514c06356bSdh142964 
5524c06356bSdh142964 	if (rval)
5534c06356bSdh142964 		smp_failed++;
554936b7af6Sjw149990 	return (rval);
555936b7af6Sjw149990 }
556936b7af6Sjw149990 
557936b7af6Sjw149990 /*ARGSUSED*/
558936b7af6Sjw149990 static int
559936b7af6Sjw149990 smp_ioctl(dev_t dev,
560936b7af6Sjw149990     int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
561936b7af6Sjw149990 {
562936b7af6Sjw149990 	int rval = 0;
563936b7af6Sjw149990 
564936b7af6Sjw149990 	switch (cmd) {
565936b7af6Sjw149990 	case USMPFUNC:
566936b7af6Sjw149990 		/*
567936b7af6Sjw149990 		 * The response payload is valid only if return value is 0
568936b7af6Sjw149990 		 * or EOVERFLOW.
569936b7af6Sjw149990 		 */
570936b7af6Sjw149990 		rval = smp_handle_func(dev, arg, flag, cred_p, rval_p);
571936b7af6Sjw149990 		break;
572936b7af6Sjw149990 	default:
573936b7af6Sjw149990 		rval = EINVAL;
574936b7af6Sjw149990 	}
575936b7af6Sjw149990 	return (rval);
576936b7af6Sjw149990 }
577936b7af6Sjw149990 
578936b7af6Sjw149990 static void
579936b7af6Sjw149990 smp_log(smp_state_t *smp_state, int level, const char *fmt, ...)
580936b7af6Sjw149990 {
581936b7af6Sjw149990 	va_list	ap;
582936b7af6Sjw149990 	char buf[256];
583936b7af6Sjw149990 	dev_info_t *dip;
584936b7af6Sjw149990 
585936b7af6Sjw149990 	if (smp_state == (smp_state_t *)NULL) {
586936b7af6Sjw149990 		dip = NULL;
587936b7af6Sjw149990 	} else {
588*96c4a178SChris Horne 		dip = smp_state->smp_sd->smp_sd_dev;
589936b7af6Sjw149990 	}
590936b7af6Sjw149990 
591936b7af6Sjw149990 	va_start(ap, fmt);
592936b7af6Sjw149990 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
593936b7af6Sjw149990 	va_end(ap);
594936b7af6Sjw149990 
595936b7af6Sjw149990 	scsi_log(dip, "smp", level, "%s", buf);
596936b7af6Sjw149990 }
597