1ac88567aSHyon Kim /*
2ac88567aSHyon Kim  * CDDL HEADER START
3ac88567aSHyon Kim  *
4ac88567aSHyon Kim  * The contents of this file are subject to the terms of the
5ac88567aSHyon Kim  * Common Development and Distribution License (the "License").
6ac88567aSHyon Kim  * You may not use this file except in compliance with the License.
7ac88567aSHyon Kim  *
8ac88567aSHyon Kim  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ac88567aSHyon Kim  * or http://www.opensolaris.org/os/licensing.
10ac88567aSHyon Kim  * See the License for the specific language governing permissions
11ac88567aSHyon Kim  * and limitations under the License.
12ac88567aSHyon Kim  *
13ac88567aSHyon Kim  * When distributing Covered Code, include this CDDL HEADER in each
14ac88567aSHyon Kim  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ac88567aSHyon Kim  * If applicable, add the following below this CDDL HEADER, with the
16ac88567aSHyon Kim  * fields enclosed by brackets "[]" replaced with your own identifying
17ac88567aSHyon Kim  * information: Portions Copyright [yyyy] [name of copyright owner]
18ac88567aSHyon Kim  *
19ac88567aSHyon Kim  * CDDL HEADER END
20ac88567aSHyon Kim  */
21ac88567aSHyon Kim 
22ac88567aSHyon Kim /*
23ac88567aSHyon Kim  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24ac88567aSHyon Kim  */
25ac88567aSHyon Kim 
26ac88567aSHyon Kim #include <sys/types.h>
27ac88567aSHyon Kim #include <sys/isa_defs.h>
28ac88567aSHyon Kim #include <sys/systeminfo.h>
29ac88567aSHyon Kim #include <sys/scsi/generic/smp_frames.h>
30ac88567aSHyon Kim 
31ac88567aSHyon Kim #include <stdio.h>
32ac88567aSHyon Kim #include <stdlib.h>
33ac88567aSHyon Kim #include <stddef.h>
34ac88567aSHyon Kim #include <string.h>
35ac88567aSHyon Kim #include <strings.h>
36ac88567aSHyon Kim #include <dlfcn.h>
37ac88567aSHyon Kim #include <limits.h>
38ac88567aSHyon Kim #include <pthread.h>
39ac88567aSHyon Kim #include <synch.h>
40ac88567aSHyon Kim 
41ac88567aSHyon Kim #include <scsi/libsmp.h>
42ac88567aSHyon Kim #include "smp_impl.h"
43ac88567aSHyon Kim 
44ac88567aSHyon Kim static pthread_mutex_t _libsmp_lock = PTHREAD_MUTEX_INITIALIZER;
45ac88567aSHyon Kim static smp_engine_t *_libsmp_engines;
46ac88567aSHyon Kim static int _libsmp_refcnt;
47ac88567aSHyon Kim 
48ac88567aSHyon Kim static boolean_t _libsmp_engine_dlclose;
49ac88567aSHyon Kim 
50ac88567aSHyon Kim static void
smp_engine_free(smp_engine_t * ep)51ac88567aSHyon Kim smp_engine_free(smp_engine_t *ep)
52ac88567aSHyon Kim {
53ac88567aSHyon Kim 	if (ep == NULL)
54ac88567aSHyon Kim 		return;
55ac88567aSHyon Kim 
56ac88567aSHyon Kim 	smp_free(ep->se_name);
57ac88567aSHyon Kim 	smp_free(ep);
58ac88567aSHyon Kim }
59ac88567aSHyon Kim 
60ac88567aSHyon Kim static void
smp_engine_destroy(smp_engine_t * ep)61ac88567aSHyon Kim smp_engine_destroy(smp_engine_t *ep)
62ac88567aSHyon Kim {
63ac88567aSHyon Kim 	smp_engine_t **pp;
64ac88567aSHyon Kim 
65ac88567aSHyon Kim 	ASSERT(MUTEX_HELD(&_libsmp_lock));
66ac88567aSHyon Kim 
67ac88567aSHyon Kim 	if (ep->se_fini != NULL)
68ac88567aSHyon Kim 		ep->se_fini(ep);
69ac88567aSHyon Kim 
70ac88567aSHyon Kim 	if (_libsmp_engine_dlclose)
71ac88567aSHyon Kim 		(void) dlclose(ep->se_object);
72ac88567aSHyon Kim 
73ac88567aSHyon Kim 	ASSERT(ep->se_refcnt == 0);
74ac88567aSHyon Kim 	for (pp = &_libsmp_engines; *pp != NULL; pp = &((*pp)->se_next))
75ac88567aSHyon Kim 		if (*pp == ep)
76ac88567aSHyon Kim 			break;
77ac88567aSHyon Kim 
78ac88567aSHyon Kim 	if (*pp != NULL)
79ac88567aSHyon Kim 		*pp = (*pp)->se_next;
80ac88567aSHyon Kim 
81ac88567aSHyon Kim 	smp_engine_free(ep);
82ac88567aSHyon Kim }
83ac88567aSHyon Kim 
84ac88567aSHyon Kim void
smp_engine_init(void)85ac88567aSHyon Kim smp_engine_init(void)
86ac88567aSHyon Kim {
87ac88567aSHyon Kim 	(void) pthread_mutex_lock(&_libsmp_lock);
88ac88567aSHyon Kim 	++_libsmp_refcnt;
89ac88567aSHyon Kim 	(void) pthread_mutex_unlock(&_libsmp_lock);
90ac88567aSHyon Kim }
91ac88567aSHyon Kim 
92ac88567aSHyon Kim void
smp_engine_fini(void)93ac88567aSHyon Kim smp_engine_fini(void)
94ac88567aSHyon Kim {
95ac88567aSHyon Kim 	smp_engine_t *ep;
96ac88567aSHyon Kim 
97ac88567aSHyon Kim 	(void) pthread_mutex_lock(&_libsmp_lock);
98ac88567aSHyon Kim 	ASSERT(_libsmp_refcnt > 0);
99ac88567aSHyon Kim 	if (--_libsmp_refcnt == 0) {
100ac88567aSHyon Kim 		while (_libsmp_engines != NULL) {
101ac88567aSHyon Kim 			ep = _libsmp_engines;
102ac88567aSHyon Kim 			_libsmp_engines = ep->se_next;
103ac88567aSHyon Kim 			smp_engine_destroy(ep);
104ac88567aSHyon Kim 		}
105ac88567aSHyon Kim 	}
106ac88567aSHyon Kim 	(void) pthread_mutex_unlock(&_libsmp_lock);
107ac88567aSHyon Kim }
108ac88567aSHyon Kim 
109ac88567aSHyon Kim static int
smp_engine_loadone(const char * path)110ac88567aSHyon Kim smp_engine_loadone(const char *path)
111ac88567aSHyon Kim {
112ac88567aSHyon Kim 	smp_engine_t *ep;
113ac88567aSHyon Kim 	void *obj;
114ac88567aSHyon Kim 
115ac88567aSHyon Kim 	ASSERT(MUTEX_HELD(&_libsmp_lock));
116ac88567aSHyon Kim 
117ac88567aSHyon Kim 	if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
118ac88567aSHyon Kim 		return (smp_set_errno(ESMP_NOENGINE));
119ac88567aSHyon Kim 
120ac88567aSHyon Kim 	if ((ep = smp_zalloc(sizeof (smp_engine_t))) == NULL) {
121ac88567aSHyon Kim 		(void) dlclose(obj);
122ac88567aSHyon Kim 		return (-1);
123ac88567aSHyon Kim 	}
124ac88567aSHyon Kim 
125ac88567aSHyon Kim 	ep->se_object = obj;
126ac88567aSHyon Kim 	ep->se_init = (int (*)())dlsym(obj, "_smp_init");
127ac88567aSHyon Kim 	ep->se_fini = (void (*)())dlsym(obj, "_smp_fini");
128ac88567aSHyon Kim 
129ac88567aSHyon Kim 	if (ep->se_init == NULL) {
130ac88567aSHyon Kim 		smp_engine_free(ep);
131ac88567aSHyon Kim 		return (smp_set_errno(ESMP_BADENGINE));
132ac88567aSHyon Kim 	}
133ac88567aSHyon Kim 
134ac88567aSHyon Kim 	if (ep->se_init(ep) != 0) {
135ac88567aSHyon Kim 		smp_engine_free(ep);
136ac88567aSHyon Kim 		return (-1);
137ac88567aSHyon Kim 	}
138ac88567aSHyon Kim 
139ac88567aSHyon Kim 	return (0);
140ac88567aSHyon Kim }
141ac88567aSHyon Kim 
142ac88567aSHyon Kim int
smp_engine_register(smp_engine_t * ep,int version,const smp_engine_config_t * ecp)143ac88567aSHyon Kim smp_engine_register(smp_engine_t *ep, int version,
144ac88567aSHyon Kim     const smp_engine_config_t *ecp)
145ac88567aSHyon Kim {
146ac88567aSHyon Kim 	ASSERT(MUTEX_HELD(&_libsmp_lock));
147ac88567aSHyon Kim 
148ac88567aSHyon Kim 	if (version != LIBSMP_ENGINE_VERSION)
149ac88567aSHyon Kim 		return (smp_set_errno(ESMP_VERSION));
150ac88567aSHyon Kim 
151ac88567aSHyon Kim 	ep->se_ops = ecp->sec_ops;
152ac88567aSHyon Kim 	ep->se_name = smp_strdup(ecp->sec_name);
153ac88567aSHyon Kim 
154ac88567aSHyon Kim 	if (ep->se_name == NULL)
155ac88567aSHyon Kim 		return (-1);
156ac88567aSHyon Kim 
157ac88567aSHyon Kim 	ep->se_next = _libsmp_engines;
158ac88567aSHyon Kim 	_libsmp_engines = ep;
159ac88567aSHyon Kim 
160ac88567aSHyon Kim 	return (0);
161ac88567aSHyon Kim }
162ac88567aSHyon Kim 
163ac88567aSHyon Kim static smp_engine_t *
smp_engine_hold_cached(const char * name)164ac88567aSHyon Kim smp_engine_hold_cached(const char *name)
165ac88567aSHyon Kim {
166ac88567aSHyon Kim 	smp_engine_t *ep;
167ac88567aSHyon Kim 
168ac88567aSHyon Kim 	ASSERT(MUTEX_HELD(&_libsmp_lock));
169ac88567aSHyon Kim 
170ac88567aSHyon Kim 	for (ep = _libsmp_engines; ep != NULL; ep = ep->se_next) {
171ac88567aSHyon Kim 		if (strcmp(ep->se_name, name) == 0) {
172ac88567aSHyon Kim 			++ep->se_refcnt;
173ac88567aSHyon Kim 			return (ep);
174ac88567aSHyon Kim 		}
175ac88567aSHyon Kim 	}
176ac88567aSHyon Kim 
177ac88567aSHyon Kim 	(void) smp_set_errno(ESMP_NOENGINE);
178ac88567aSHyon Kim 	return (NULL);
179ac88567aSHyon Kim }
180ac88567aSHyon Kim 
181ac88567aSHyon Kim static smp_engine_t *
smp_engine_hold(const char * name)182ac88567aSHyon Kim smp_engine_hold(const char *name)
183ac88567aSHyon Kim {
184ac88567aSHyon Kim 	smp_engine_t *ep;
185ac88567aSHyon Kim 	const char *pluginpath, *p, *q;
186ac88567aSHyon Kim 	char pluginroot[PATH_MAX];
187ac88567aSHyon Kim 	char path[PATH_MAX];
188ac88567aSHyon Kim 	char isa[257];
189ac88567aSHyon Kim 
190ac88567aSHyon Kim 	(void) pthread_mutex_lock(&_libsmp_lock);
191ac88567aSHyon Kim 	ep = smp_engine_hold_cached(name);
192ac88567aSHyon Kim 	if (ep != NULL) {
193ac88567aSHyon Kim 		(void) pthread_mutex_unlock(&_libsmp_lock);
194ac88567aSHyon Kim 		return (ep);
195ac88567aSHyon Kim 	}
196ac88567aSHyon Kim 
197ac88567aSHyon Kim #if defined(_LP64)
198ac88567aSHyon Kim 	if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
199ac88567aSHyon Kim 		isa[0] = '\0';
200ac88567aSHyon Kim #else
201ac88567aSHyon Kim 	isa[0] = '\0';
202ac88567aSHyon Kim #endif
203ac88567aSHyon Kim 
204ac88567aSHyon Kim 	if ((pluginpath = getenv("SMP_PLUGINPATH")) == NULL)
205ac88567aSHyon Kim 		pluginpath = LIBSMP_DEFAULT_PLUGINDIR;
206ac88567aSHyon Kim 
207ac88567aSHyon Kim 	_libsmp_engine_dlclose = (getenv("SMP_NODLCLOSE") == NULL);
208ac88567aSHyon Kim 
209ac88567aSHyon Kim 	for (p = pluginpath; p != NULL; p = q) {
210ac88567aSHyon Kim 		if ((q = strchr(p, ':')) != NULL) {
211ac88567aSHyon Kim 			ptrdiff_t len = q - p;
212ac88567aSHyon Kim 			(void) strncpy(pluginroot, p, len);
213ac88567aSHyon Kim 			pluginroot[len] = '\0';
214ac88567aSHyon Kim 			while (*q == ':')
215ac88567aSHyon Kim 				++q;
216ac88567aSHyon Kim 			if (*q == '\0')
217ac88567aSHyon Kim 				q = NULL;
218ac88567aSHyon Kim 			if (len == 0)
219ac88567aSHyon Kim 				continue;
220ac88567aSHyon Kim 		} else {
221ac88567aSHyon Kim 			(void) strcpy(pluginroot, p);
222ac88567aSHyon Kim 		}
223ac88567aSHyon Kim 
224ac88567aSHyon Kim 		if (pluginroot[0] != '/')
225ac88567aSHyon Kim 			continue;
226ac88567aSHyon Kim 
227ac88567aSHyon Kim 		(void) snprintf(path, PATH_MAX, "%s/%s/%s/%s%s",
228ac88567aSHyon Kim 		    pluginroot, LIBSMP_PLUGIN_ENGINE,
229ac88567aSHyon Kim 		    isa, name, LIBSMP_PLUGIN_EXT);
230ac88567aSHyon Kim 
231ac88567aSHyon Kim 		if (smp_engine_loadone(path) == 0) {
232ac88567aSHyon Kim 			ep = smp_engine_hold_cached(name);
233ac88567aSHyon Kim 			(void) pthread_mutex_unlock(&_libsmp_lock);
234ac88567aSHyon Kim 			return (ep);
235ac88567aSHyon Kim 		}
236ac88567aSHyon Kim 	}
237ac88567aSHyon Kim 
238ac88567aSHyon Kim 	return (NULL);
239ac88567aSHyon Kim }
240ac88567aSHyon Kim 
241ac88567aSHyon Kim static void
smp_engine_rele(smp_engine_t * ep)242ac88567aSHyon Kim smp_engine_rele(smp_engine_t *ep)
243ac88567aSHyon Kim {
244ac88567aSHyon Kim 	(void) pthread_mutex_lock(&_libsmp_lock);
245ac88567aSHyon Kim 	ASSERT(ep->se_refcnt > 0);
246ac88567aSHyon Kim 	--ep->se_refcnt;
247ac88567aSHyon Kim 	(void) pthread_mutex_unlock(&_libsmp_lock);
248ac88567aSHyon Kim }
249ac88567aSHyon Kim 
250ac88567aSHyon Kim static void
smp_parse_mtbf(const char * envvar,uint_t * intp)251ac88567aSHyon Kim smp_parse_mtbf(const char *envvar, uint_t *intp)
252ac88567aSHyon Kim {
253ac88567aSHyon Kim 	const char *strval;
254ac88567aSHyon Kim 	int intval;
255ac88567aSHyon Kim 
256ac88567aSHyon Kim 	if ((strval = getenv(envvar)) != NULL &&
257ac88567aSHyon Kim 	    (intval = atoi(strval)) > 0) {
258ac88567aSHyon Kim 		srand48(gethrtime());
259ac88567aSHyon Kim 		*intp = intval;
260ac88567aSHyon Kim 	}
261ac88567aSHyon Kim }
262ac88567aSHyon Kim 
263ac88567aSHyon Kim smp_target_t *
smp_open(const smp_target_def_t * tdp)264ac88567aSHyon Kim smp_open(const smp_target_def_t *tdp)
265ac88567aSHyon Kim {
266ac88567aSHyon Kim 	smp_engine_t *ep;
267ac88567aSHyon Kim 	smp_target_t *tp;
268ac88567aSHyon Kim 	void *private;
269ac88567aSHyon Kim 	const char *engine;
270ac88567aSHyon Kim 
271ac88567aSHyon Kim 	if ((engine = tdp->std_engine) == NULL) {
272ac88567aSHyon Kim 		if ((engine = getenv("LIBSMP_DEFAULT_ENGINE")) == NULL)
273ac88567aSHyon Kim 			engine = LIBSMP_DEFAULT_ENGINE;
274ac88567aSHyon Kim 	}
275ac88567aSHyon Kim 
276ac88567aSHyon Kim 	if ((ep = smp_engine_hold(engine)) == NULL)
277ac88567aSHyon Kim 		return (NULL);
278ac88567aSHyon Kim 
279ac88567aSHyon Kim 	if ((tp = smp_zalloc(sizeof (smp_target_t))) == NULL) {
280ac88567aSHyon Kim 		smp_engine_rele(ep);
281ac88567aSHyon Kim 		return (NULL);
282ac88567aSHyon Kim 	}
283ac88567aSHyon Kim 
284ac88567aSHyon Kim 	if ((private = ep->se_ops->seo_open(tdp->std_def)) == NULL) {
285ac88567aSHyon Kim 		smp_engine_rele(ep);
286ac88567aSHyon Kim 		smp_free(tp);
287ac88567aSHyon Kim 		return (NULL);
288ac88567aSHyon Kim 	}
289ac88567aSHyon Kim 
290ac88567aSHyon Kim 	smp_parse_mtbf("LIBSMP_MTBF_REQUEST", &tp->st_mtbf_request);
291ac88567aSHyon Kim 	smp_parse_mtbf("LIBSMP_MTBF_RESPONSE", &tp->st_mtbf_response);
292ac88567aSHyon Kim 
293ac88567aSHyon Kim 	tp->st_engine = ep;
294ac88567aSHyon Kim 	tp->st_priv = private;
295ac88567aSHyon Kim 
296ac88567aSHyon Kim 	if (smp_plugin_load(tp) != 0) {
297ac88567aSHyon Kim 		smp_close(tp);
298ac88567aSHyon Kim 		return (NULL);
299ac88567aSHyon Kim 	}
300ac88567aSHyon Kim 
301ac88567aSHyon Kim 	return (tp);
302ac88567aSHyon Kim }
303ac88567aSHyon Kim 
304ac88567aSHyon Kim void
smp_target_name(const smp_target_t * tp,char * buf,size_t len)305ac88567aSHyon Kim smp_target_name(const smp_target_t *tp, char *buf, size_t len)
306ac88567aSHyon Kim {
307ac88567aSHyon Kim 	tp->st_engine->se_ops->seo_target_name(tp->st_priv, buf, len);
308ac88567aSHyon Kim }
309ac88567aSHyon Kim 
310ac88567aSHyon Kim uint64_t
smp_target_addr(const smp_target_t * tp)311ac88567aSHyon Kim smp_target_addr(const smp_target_t *tp)
312ac88567aSHyon Kim {
313ac88567aSHyon Kim 	return (tp->st_engine->se_ops->seo_target_addr(tp->st_priv));
314ac88567aSHyon Kim }
315ac88567aSHyon Kim 
316ac88567aSHyon Kim const char *
smp_target_vendor(const smp_target_t * tp)317ac88567aSHyon Kim smp_target_vendor(const smp_target_t *tp)
318ac88567aSHyon Kim {
319ac88567aSHyon Kim 	return (tp->st_vendor);
320ac88567aSHyon Kim }
321ac88567aSHyon Kim 
322ac88567aSHyon Kim const char *
smp_target_product(const smp_target_t * tp)323ac88567aSHyon Kim smp_target_product(const smp_target_t *tp)
324ac88567aSHyon Kim {
325ac88567aSHyon Kim 	return (tp->st_product);
326ac88567aSHyon Kim }
327ac88567aSHyon Kim 
328ac88567aSHyon Kim const char *
smp_target_revision(const smp_target_t * tp)329ac88567aSHyon Kim smp_target_revision(const smp_target_t *tp)
330ac88567aSHyon Kim {
331ac88567aSHyon Kim 	return (tp->st_revision);
332ac88567aSHyon Kim }
333ac88567aSHyon Kim 
334ac88567aSHyon Kim const char *
smp_target_component_vendor(const smp_target_t * tp)335ac88567aSHyon Kim smp_target_component_vendor(const smp_target_t *tp)
336ac88567aSHyon Kim {
337ac88567aSHyon Kim 	return (tp->st_component_vendor);
338ac88567aSHyon Kim }
339ac88567aSHyon Kim 
340ac88567aSHyon Kim uint16_t
smp_target_component_id(const smp_target_t * tp)341ac88567aSHyon Kim smp_target_component_id(const smp_target_t *tp)
342ac88567aSHyon Kim {
343ac88567aSHyon Kim 	return (tp->st_component_id);
344ac88567aSHyon Kim }
345ac88567aSHyon Kim 
346ac88567aSHyon Kim uint8_t
smp_target_component_revision(const smp_target_t * tp)347ac88567aSHyon Kim smp_target_component_revision(const smp_target_t *tp)
348ac88567aSHyon Kim {
349ac88567aSHyon Kim 	return (tp->st_component_revision);
350ac88567aSHyon Kim }
351ac88567aSHyon Kim 
352ac88567aSHyon Kim uint_t
smp_target_getcap(const smp_target_t * tp)353ac88567aSHyon Kim smp_target_getcap(const smp_target_t *tp)
354ac88567aSHyon Kim {
355ac88567aSHyon Kim 	uint_t cap = 0;
356ac88567aSHyon Kim 
357ac88567aSHyon Kim 	if (tp->st_repgen.srgr_long_response)
358ac88567aSHyon Kim 		cap |= SMP_TARGET_C_LONG_RESP;
359ac88567aSHyon Kim 
360ac88567aSHyon Kim 	if (tp->st_repgen.srgr_zoning_supported)
361ac88567aSHyon Kim 		cap |= SMP_TARGET_C_ZONING;
362ac88567aSHyon Kim 
363ac88567aSHyon Kim 	if (tp->st_repgen.srgr_number_of_zone_grps == SMP_ZONE_GROUPS_256)
364ac88567aSHyon Kim 		cap |= SMP_TARGET_C_ZG_256;
365ac88567aSHyon Kim 
366ac88567aSHyon Kim 	return (cap);
367ac88567aSHyon Kim }
368ac88567aSHyon Kim 
369ac88567aSHyon Kim void
smp_target_set_change_count(smp_target_t * tp,uint16_t cc)370ac88567aSHyon Kim smp_target_set_change_count(smp_target_t *tp, uint16_t cc)
371ac88567aSHyon Kim {
372ac88567aSHyon Kim 	tp->st_change_count = cc;
373ac88567aSHyon Kim }
374ac88567aSHyon Kim 
375ac88567aSHyon Kim uint16_t
smp_target_get_change_count(const smp_target_t * tp)376ac88567aSHyon Kim smp_target_get_change_count(const smp_target_t *tp)
377ac88567aSHyon Kim {
378ac88567aSHyon Kim 	return (tp->st_change_count);
379ac88567aSHyon Kim }
380ac88567aSHyon Kim 
381*d0698e0dSDavid Hollister uint8_t
smp_target_get_number_of_phys(const smp_target_t * tp)382*d0698e0dSDavid Hollister smp_target_get_number_of_phys(const smp_target_t *tp)
383*d0698e0dSDavid Hollister {
384*d0698e0dSDavid Hollister 	return (tp->st_repgen.srgr_number_of_phys);
385*d0698e0dSDavid Hollister }
386*d0698e0dSDavid Hollister 
387*d0698e0dSDavid Hollister uint16_t
smp_target_get_exp_route_indexes(const smp_target_t * tp)388*d0698e0dSDavid Hollister smp_target_get_exp_route_indexes(const smp_target_t *tp)
389*d0698e0dSDavid Hollister {
390*d0698e0dSDavid Hollister 	return (tp->st_repgen.srgr_exp_route_indexes);
391*d0698e0dSDavid Hollister }
392*d0698e0dSDavid Hollister 
393ac88567aSHyon Kim void
smp_close(smp_target_t * tp)394ac88567aSHyon Kim smp_close(smp_target_t *tp)
395ac88567aSHyon Kim {
396ac88567aSHyon Kim 	smp_free(tp->st_vendor);
397ac88567aSHyon Kim 	smp_free(tp->st_product);
398ac88567aSHyon Kim 	smp_free(tp->st_revision);
399ac88567aSHyon Kim 	smp_free(tp->st_component_vendor);
400ac88567aSHyon Kim 
401ac88567aSHyon Kim 	smp_plugin_unload(tp);
402ac88567aSHyon Kim 
403ac88567aSHyon Kim 	tp->st_engine->se_ops->seo_close(tp->st_priv);
404ac88567aSHyon Kim 	smp_engine_rele(tp->st_engine);
405ac88567aSHyon Kim 
406ac88567aSHyon Kim 	smp_free(tp);
407ac88567aSHyon Kim }
408ac88567aSHyon Kim 
409ac88567aSHyon Kim /*
410ac88567aSHyon Kim  * Set the timeout in seconds for this action.  If no timeout is specified
411ac88567aSHyon Kim  * or if the timeout is set to 0, an implementation-specific timeout will be
412ac88567aSHyon Kim  * used (which may vary based on the target, command or other variables).
413ac88567aSHyon Kim  * Not all engines support all timeout values.  Setting the timeout to a value
414ac88567aSHyon Kim  * not supported by the engine will cause engine-defined behavior when the
415ac88567aSHyon Kim  * action is executed.
416ac88567aSHyon Kim  */
417ac88567aSHyon Kim void
smp_action_set_timeout(smp_action_t * ap,uint32_t timeout)418ac88567aSHyon Kim smp_action_set_timeout(smp_action_t *ap, uint32_t timeout)
419ac88567aSHyon Kim {
420ac88567aSHyon Kim 	ap->sa_timeout = timeout;
421ac88567aSHyon Kim }
422ac88567aSHyon Kim 
423ac88567aSHyon Kim /*
424ac88567aSHyon Kim  * Obtain the timeout setting for this action.
425ac88567aSHyon Kim  */
426ac88567aSHyon Kim uint32_t
smp_action_get_timeout(const smp_action_t * ap)427ac88567aSHyon Kim smp_action_get_timeout(const smp_action_t *ap)
428ac88567aSHyon Kim {
429ac88567aSHyon Kim 	return (ap->sa_timeout);
430ac88567aSHyon Kim }
431ac88567aSHyon Kim 
432ac88567aSHyon Kim const smp_function_def_t *
smp_action_get_function_def(const smp_action_t * ap)433ac88567aSHyon Kim smp_action_get_function_def(const smp_action_t *ap)
434ac88567aSHyon Kim {
435ac88567aSHyon Kim 	return (ap->sa_def);
436ac88567aSHyon Kim }
437ac88567aSHyon Kim 
438ac88567aSHyon Kim /*
439ac88567aSHyon Kim  * Obtain the user-requested request allocation size.  Note that the
440ac88567aSHyon Kim  * interpretation of this is function-dependent.
441ac88567aSHyon Kim  */
442ac88567aSHyon Kim size_t
smp_action_get_rqsd(const smp_action_t * ap)443ac88567aSHyon Kim smp_action_get_rqsd(const smp_action_t *ap)
444ac88567aSHyon Kim {
445ac88567aSHyon Kim 	return (ap->sa_request_rqsd);
446ac88567aSHyon Kim }
447ac88567aSHyon Kim 
448ac88567aSHyon Kim /*
449ac88567aSHyon Kim  * Obtains the address and amount of space allocated for the portion of the
450ac88567aSHyon Kim  * request data that lies between the header (if any) and the CRC.
451ac88567aSHyon Kim  */
452ac88567aSHyon Kim void
smp_action_get_request(const smp_action_t * ap,void ** reqp,size_t * dlenp)453ac88567aSHyon Kim smp_action_get_request(const smp_action_t *ap, void **reqp, size_t *dlenp)
454ac88567aSHyon Kim {
455ac88567aSHyon Kim 	if (reqp != NULL) {
456ac88567aSHyon Kim 		if (ap->sa_request_data_off >= 0) {
457ac88567aSHyon Kim 			*reqp = ap->sa_request + ap->sa_request_data_off;
458ac88567aSHyon Kim 		} else {
459ac88567aSHyon Kim 			*reqp = NULL;
460ac88567aSHyon Kim 		}
461ac88567aSHyon Kim 	}
462ac88567aSHyon Kim 
463ac88567aSHyon Kim 	if (dlenp != NULL)
464ac88567aSHyon Kim 		*dlenp = ap->sa_request_alloc_len -
465ac88567aSHyon Kim 		    (ap->sa_request_data_off + sizeof (smp_crc_t));
466ac88567aSHyon Kim }
467ac88567aSHyon Kim 
468ac88567aSHyon Kim /*
469ac88567aSHyon Kim  * Obtains the address and amount of valid response data (that part of the
470ac88567aSHyon Kim  * response frame, if any, that lies between the header and the CRC).  The
471ac88567aSHyon Kim  * result, if any, is also returned in the location pointed to by result.
472ac88567aSHyon Kim  */
473ac88567aSHyon Kim void
smp_action_get_response(const smp_action_t * ap,smp_result_t * resultp,void ** respp,size_t * dlenp)474ac88567aSHyon Kim smp_action_get_response(const smp_action_t *ap, smp_result_t *resultp,
475ac88567aSHyon Kim     void **respp, size_t *dlenp)
476ac88567aSHyon Kim {
477ac88567aSHyon Kim 	if (resultp != NULL)
478ac88567aSHyon Kim 		*resultp = ap->sa_result;
479ac88567aSHyon Kim 
480ac88567aSHyon Kim 	if (respp != NULL)
481ac88567aSHyon Kim 		*respp = (ap->sa_response_data_len > 0) ?
482ac88567aSHyon Kim 		    (ap->sa_response + ap->sa_response_data_off) : NULL;
483ac88567aSHyon Kim 
484ac88567aSHyon Kim 	if (dlenp != NULL)
485ac88567aSHyon Kim 		*dlenp = ap->sa_response_data_len;
486ac88567aSHyon Kim }
487ac88567aSHyon Kim 
488ac88567aSHyon Kim /*
489ac88567aSHyon Kim  * Obtains the entire request frame and the amount of space allocated for it.
490ac88567aSHyon Kim  * This is intended only for use by plugins; front-end consumers should use
491ac88567aSHyon Kim  * smp_action_get_request() instead.
492ac88567aSHyon Kim  */
493ac88567aSHyon Kim void
smp_action_get_request_frame(const smp_action_t * ap,void ** reqp,size_t * alenp)494ac88567aSHyon Kim smp_action_get_request_frame(const smp_action_t *ap, void **reqp, size_t *alenp)
495ac88567aSHyon Kim {
496ac88567aSHyon Kim 	if (reqp != NULL)
497ac88567aSHyon Kim 		*reqp = ap->sa_request;
498ac88567aSHyon Kim 
499ac88567aSHyon Kim 	if (alenp != NULL)
500ac88567aSHyon Kim 		*alenp = ap->sa_request_alloc_len;
501ac88567aSHyon Kim }
502ac88567aSHyon Kim 
503ac88567aSHyon Kim /*
504ac88567aSHyon Kim  * Obtains the entire response frame and the amount of space allocated for it.
505ac88567aSHyon Kim  * This is intended only for use by plugins; front-end consumers should use
506ac88567aSHyon Kim  * smp_action_get_response() instead.
507ac88567aSHyon Kim  */
508ac88567aSHyon Kim void
smp_action_get_response_frame(const smp_action_t * ap,void ** respp,size_t * lenp)509ac88567aSHyon Kim smp_action_get_response_frame(const smp_action_t *ap,
510ac88567aSHyon Kim     void **respp, size_t *lenp)
511ac88567aSHyon Kim {
512ac88567aSHyon Kim 	if (respp != NULL)
513ac88567aSHyon Kim 		*respp = ap->sa_response;
514ac88567aSHyon Kim 
515ac88567aSHyon Kim 	if (lenp != NULL) {
516ac88567aSHyon Kim 		if (ap->sa_flags & SMP_ACTION_F_EXEC)
517ac88567aSHyon Kim 			*lenp = ap->sa_response_engine_len;
518ac88567aSHyon Kim 		else
519ac88567aSHyon Kim 			*lenp = ap->sa_response_alloc_len;
520ac88567aSHyon Kim 	}
521ac88567aSHyon Kim }
522ac88567aSHyon Kim 
523ac88567aSHyon Kim /*
524ac88567aSHyon Kim  * Set the total response frame length as determined by the engine.  This
525ac88567aSHyon Kim  * should never be called by consumers or plugins other than engines.
526ac88567aSHyon Kim  */
527ac88567aSHyon Kim void
smp_action_set_response_len(smp_action_t * ap,size_t elen)528ac88567aSHyon Kim smp_action_set_response_len(smp_action_t *ap, size_t elen)
529ac88567aSHyon Kim {
530ac88567aSHyon Kim 	ap->sa_response_engine_len = elen;
531ac88567aSHyon Kim }
532ac88567aSHyon Kim 
533ac88567aSHyon Kim void
smp_action_set_result(smp_action_t * ap,smp_result_t result)534ac88567aSHyon Kim smp_action_set_result(smp_action_t *ap, smp_result_t result)
535ac88567aSHyon Kim {
536ac88567aSHyon Kim 	ap->sa_result = result;
537ac88567aSHyon Kim }
538ac88567aSHyon Kim 
539ac88567aSHyon Kim /*
540ac88567aSHyon Kim  * Allocate an action object.  The object will contain a request buffer
541ac88567aSHyon Kim  * to hold the frame to be transmitted to the target, a response buffer
542ac88567aSHyon Kim  * for the frame to be received from it, and auxiliary private information.
543ac88567aSHyon Kim  *
544ac88567aSHyon Kim  * For the request, callers may specify:
545ac88567aSHyon Kim  *
546ac88567aSHyon Kim  * - An externally-allocated buffer and its size in bytes, or
547ac88567aSHyon Kim  * - NULL and a function-specific size descriptor, or
548ac88567aSHyon Kim  *
549ac88567aSHyon Kim  * Note that for some functions, the size descriptor may be 0, indicating that
550ac88567aSHyon Kim  * a default buffer length will be used.  It is the caller's responsibility
551ac88567aSHyon Kim  * to correctly interpret function-specific buffer lengths.  See appropriate
552ac88567aSHyon Kim  * plugin documentation for information on buffer sizes and buffer content
553ac88567aSHyon Kim  * interpretation.
554ac88567aSHyon Kim  *
555ac88567aSHyon Kim  * For the response, callers may specify:
556ac88567aSHyon Kim  *
557ac88567aSHyon Kim  * - An externally-allocated buffer and its size in bytes, or
558ac88567aSHyon Kim  * - NULL and 0, to use a guaranteed-sufficient buffer.
559ac88567aSHyon Kim  *
560ac88567aSHyon Kim  * If an invalid request size descriptor is provided, or a preallocated
561ac88567aSHyon Kim  * buffer is provided and it is insufficiently large, this function will
562ac88567aSHyon Kim  * fail with ESMP_RANGE.
563ac88567aSHyon Kim  *
564ac88567aSHyon Kim  * Callers are discouraged from allocating their own buffers and must be
565ac88567aSHyon Kim  * aware of the consequences of specifying non-default lengths.
566ac88567aSHyon Kim  */
567ac88567aSHyon Kim smp_action_t *
smp_action_xalloc(smp_function_t fn,smp_target_t * tp,void * rq,size_t rqsd,void * rs,size_t rslen)568ac88567aSHyon Kim smp_action_xalloc(smp_function_t fn, smp_target_t *tp,
569ac88567aSHyon Kim     void *rq, size_t rqsd, void *rs, size_t rslen)
570ac88567aSHyon Kim {
571ac88567aSHyon Kim 	smp_plugin_t *pp;
572ac88567aSHyon Kim 	const smp_function_def_t *dp = NULL;
573ac88567aSHyon Kim 	smp_action_t *ap;
574ac88567aSHyon Kim 	uint_t cap;
575ac88567aSHyon Kim 	size_t rqlen, len;
576ac88567aSHyon Kim 	uint8_t *alloc;
577ac88567aSHyon Kim 	int i;
578ac88567aSHyon Kim 
579ac88567aSHyon Kim 	cap = smp_target_getcap(tp);
580ac88567aSHyon Kim 
581ac88567aSHyon Kim 	for (pp = tp->st_plugin_first; pp != NULL; pp = pp->sp_next) {
582ac88567aSHyon Kim 		if (pp->sp_functions == NULL)
583ac88567aSHyon Kim 			continue;
584ac88567aSHyon Kim 
585ac88567aSHyon Kim 		for (i = 0; pp->sp_functions[i].sfd_rq_len != NULL; i++) {
586ac88567aSHyon Kim 			dp = &pp->sp_functions[i];
587ac88567aSHyon Kim 			if (dp->sfd_function == fn &&
588ac88567aSHyon Kim 			    ((cap & dp->sfd_capmask) == dp->sfd_capset))
589ac88567aSHyon Kim 				break;
590ac88567aSHyon Kim 		}
591ac88567aSHyon Kim 	}
592ac88567aSHyon Kim 
593ac88567aSHyon Kim 	if (dp == NULL) {
594ac88567aSHyon Kim 		(void) smp_set_errno(ESMP_BADFUNC);
595ac88567aSHyon Kim 		return (NULL);
596ac88567aSHyon Kim 	}
597ac88567aSHyon Kim 
598ac88567aSHyon Kim 	if (rq == NULL) {
599ac88567aSHyon Kim 		if ((rqlen = dp->sfd_rq_len(rqsd, tp)) == 0)
600ac88567aSHyon Kim 			return (NULL);
601ac88567aSHyon Kim 	} else if (rqlen < SMP_REQ_MINLEN) {
602ac88567aSHyon Kim 		(void) smp_set_errno(ESMP_RANGE);
603ac88567aSHyon Kim 		return (NULL);
604ac88567aSHyon Kim 	}
605ac88567aSHyon Kim 
606ac88567aSHyon Kim 	if (rs == NULL) {
607ac88567aSHyon Kim 		rslen = 1020 + SMP_RESP_MINLEN;
608ac88567aSHyon Kim 	} else if (rslen < SMP_RESP_MINLEN) {
609ac88567aSHyon Kim 		(void) smp_set_errno(ESMP_RANGE);
610ac88567aSHyon Kim 		return (NULL);
611ac88567aSHyon Kim 	}
612ac88567aSHyon Kim 
613ac88567aSHyon Kim 	len = offsetof(smp_action_t, sa_buf[0]);
614ac88567aSHyon Kim 	if (rq == NULL)
615ac88567aSHyon Kim 		len += rqlen;
616ac88567aSHyon Kim 	if (rs == NULL)
617ac88567aSHyon Kim 		len += rslen;
618ac88567aSHyon Kim 
619ac88567aSHyon Kim 	if ((ap = smp_zalloc(len)) == NULL)
620ac88567aSHyon Kim 		return (NULL);
621ac88567aSHyon Kim 
622ac88567aSHyon Kim 	ap->sa_def = dp;
623ac88567aSHyon Kim 	alloc = ap->sa_buf;
624ac88567aSHyon Kim 
625ac88567aSHyon Kim 	if (rq == NULL) {
626ac88567aSHyon Kim 		ap->sa_request = alloc;
627ac88567aSHyon Kim 		alloc += rqlen;
628ac88567aSHyon Kim 	}
629ac88567aSHyon Kim 	ap->sa_request_alloc_len = rqlen;
630ac88567aSHyon Kim 
631ac88567aSHyon Kim 	if (rs == NULL) {
632ac88567aSHyon Kim 		ap->sa_response = alloc;
633ac88567aSHyon Kim 		alloc += rslen;
634ac88567aSHyon Kim 	}
635ac88567aSHyon Kim 	ap->sa_response_alloc_len = rslen;
636ac88567aSHyon Kim 
637ac88567aSHyon Kim 	ASSERT(alloc - (uint8_t *)ap == len);
638ac88567aSHyon Kim 
639ac88567aSHyon Kim 	ap->sa_request_data_off = dp->sfd_rq_dataoff(ap, tp);
640ac88567aSHyon Kim 	ap->sa_flags |= SMP_ACTION_F_OFFSET;
641ac88567aSHyon Kim 
642ac88567aSHyon Kim 	return (ap);
643ac88567aSHyon Kim }
644ac88567aSHyon Kim 
645ac88567aSHyon Kim /*
646ac88567aSHyon Kim  * Simplified action allocator.  All buffers are allocated for the
647ac88567aSHyon Kim  * caller.  The request buffer size will be based on the function-specific
648ac88567aSHyon Kim  * interpretation of the rqsize parameter.  The response buffer size will be
649ac88567aSHyon Kim  * a function-specific value sufficiently large to capture any response.
650ac88567aSHyon Kim  */
651ac88567aSHyon Kim smp_action_t *
smp_action_alloc(smp_function_t fn,smp_target_t * tp,size_t rqsd)652ac88567aSHyon Kim smp_action_alloc(smp_function_t fn, smp_target_t *tp, size_t rqsd)
653ac88567aSHyon Kim {
654ac88567aSHyon Kim 	return (smp_action_xalloc(fn, tp, NULL, rqsd, NULL, 0));
655ac88567aSHyon Kim }
656ac88567aSHyon Kim 
657ac88567aSHyon Kim void
smp_action_free(smp_action_t * ap)658ac88567aSHyon Kim smp_action_free(smp_action_t *ap)
659ac88567aSHyon Kim {
660ac88567aSHyon Kim 	if (ap == NULL)
661ac88567aSHyon Kim 		return;
662ac88567aSHyon Kim 
663ac88567aSHyon Kim 	smp_free(ap);
664ac88567aSHyon Kim }
665ac88567aSHyon Kim 
666ac88567aSHyon Kim /*
667ac88567aSHyon Kim  * For testing purposes, we allow data to be corrupted via an environment
668ac88567aSHyon Kim  * variable setting.  This helps ensure that higher level software can cope with
669ac88567aSHyon Kim  * arbitrarily broken targets.  The mtbf value represents the number of bytes we
670ac88567aSHyon Kim  * will see, on average, in between each failure.  Therefore, for each N bytes,
671ac88567aSHyon Kim  * we would expect to see (N / mtbf) bytes of corruption.
672ac88567aSHyon Kim  */
673ac88567aSHyon Kim static void
smp_inject_errors(void * data,size_t len,uint_t mtbf)674ac88567aSHyon Kim smp_inject_errors(void *data, size_t len, uint_t mtbf)
675ac88567aSHyon Kim {
676ac88567aSHyon Kim 	char *buf = data;
677ac88567aSHyon Kim 	double prob;
678ac88567aSHyon Kim 	size_t index;
679ac88567aSHyon Kim 
680ac88567aSHyon Kim 	if (len == 0)
681ac88567aSHyon Kim 		return;
682ac88567aSHyon Kim 
683ac88567aSHyon Kim 	prob = (double)len / mtbf;
684ac88567aSHyon Kim 
685ac88567aSHyon Kim 	while (prob > 1) {
686ac88567aSHyon Kim 		index = lrand48() % len;
687ac88567aSHyon Kim 		buf[index] = (lrand48() % 256);
688ac88567aSHyon Kim 		prob -= 1;
689ac88567aSHyon Kim 	}
690ac88567aSHyon Kim 
691ac88567aSHyon Kim 	if (drand48() <= prob) {
692ac88567aSHyon Kim 		index = lrand48() % len;
693ac88567aSHyon Kim 		buf[index] = (lrand48() % 256);
694ac88567aSHyon Kim 	}
695ac88567aSHyon Kim }
696ac88567aSHyon Kim 
697ac88567aSHyon Kim int
smp_exec(smp_action_t * ap,smp_target_t * tp)698ac88567aSHyon Kim smp_exec(smp_action_t *ap, smp_target_t *tp)
699ac88567aSHyon Kim {
700ac88567aSHyon Kim 	const smp_function_def_t *dp;
701ac88567aSHyon Kim 	int ret;
702ac88567aSHyon Kim 
703ac88567aSHyon Kim 	dp = ap->sa_def;
704ac88567aSHyon Kim 	dp->sfd_rq_setframe(ap, tp);
705ac88567aSHyon Kim 
706ac88567aSHyon Kim 	if (tp->st_mtbf_request != 0) {
707ac88567aSHyon Kim 		smp_inject_errors(ap->sa_request, ap->sa_request_alloc_len,
708ac88567aSHyon Kim 		    tp->st_mtbf_request);
709ac88567aSHyon Kim 	}
710ac88567aSHyon Kim 
711ac88567aSHyon Kim 	ret = tp->st_engine->se_ops->seo_exec(tp->st_priv, ap);
712ac88567aSHyon Kim 
713ac88567aSHyon Kim 	if (ret == 0 && tp->st_mtbf_response != 0) {
714ac88567aSHyon Kim 		smp_inject_errors(ap->sa_response, ap->sa_response_engine_len,
715ac88567aSHyon Kim 		    tp->st_mtbf_response);
716ac88567aSHyon Kim 	}
717ac88567aSHyon Kim 
718ac88567aSHyon Kim 	if (ret != 0)
719ac88567aSHyon Kim 		return (ret);
720ac88567aSHyon Kim 
721ac88567aSHyon Kim 	ap->sa_flags |= SMP_ACTION_F_EXEC;
722ac88567aSHyon Kim 
723ac88567aSHyon Kim 	/*
724ac88567aSHyon Kim 	 * Obtain the data length and offset from the underlying plugins.
725ac88567aSHyon Kim 	 * Then offer the plugins the opportunity to set any parameters in the
726ac88567aSHyon Kim 	 * target to reflect state observed in the response.
727ac88567aSHyon Kim 	 */
728ac88567aSHyon Kim 	ap->sa_response_data_len = dp->sfd_rs_datalen(ap, tp);
729ac88567aSHyon Kim 	ap->sa_response_data_off = dp->sfd_rs_dataoff(ap, tp);
730ac88567aSHyon Kim 	dp->sfd_rs_getparams(ap, tp);
731ac88567aSHyon Kim 
732ac88567aSHyon Kim 	ap->sa_flags |= SMP_ACTION_F_DECODE;
733ac88567aSHyon Kim 
734ac88567aSHyon Kim 	return (0);
735ac88567aSHyon Kim }
736