xref: /onnv-gate/usr/src/lib/scsi/libses/common/ses_plugin.c (revision 13093:48f2dbca79a2)
16316Seschrock /*
26316Seschrock  * CDDL HEADER START
36316Seschrock  *
46316Seschrock  * The contents of this file are subject to the terms of the
56316Seschrock  * Common Development and Distribution License (the "License").
66316Seschrock  * You may not use this file except in compliance with the License.
76316Seschrock  *
86316Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96316Seschrock  * or http://www.opensolaris.org/os/licensing.
106316Seschrock  * See the License for the specific language governing permissions
116316Seschrock  * and limitations under the License.
126316Seschrock  *
136316Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
146316Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156316Seschrock  * If applicable, add the following below this CDDL HEADER, with the
166316Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
176316Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
186316Seschrock  *
196316Seschrock  * CDDL HEADER END
206316Seschrock  */
216316Seschrock 
226316Seschrock /*
2312126SHyon.Kim@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246316Seschrock  */
256316Seschrock 
266316Seschrock #include <scsi/libses.h>
276316Seschrock #include "ses_impl.h"
286316Seschrock 
296316Seschrock static boolean_t ses_plugin_dlclose;
306316Seschrock 
316316Seschrock /*ARGSUSED*/
326316Seschrock void *
ses_plugin_ctlpage_lookup(ses_plugin_t * sp,ses_snap_t * snap,int pagenum,size_t len,ses_node_t * np,boolean_t unique)336316Seschrock ses_plugin_ctlpage_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
346316Seschrock     size_t len, ses_node_t *np, boolean_t unique)
356316Seschrock {
366316Seschrock 	ses_target_t *tp = snap->ss_target;
376316Seschrock 	ses_snap_page_t *pp;
386316Seschrock 	ses_pagedesc_t *dp;
396316Seschrock 
406316Seschrock 	if ((pp = ses_snap_ctl_page(snap, pagenum, len, unique)) == NULL)
416316Seschrock 		return (NULL);
426316Seschrock 
436316Seschrock 	if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_CTL)) == NULL)
446316Seschrock 		return (NULL);
456316Seschrock 
466316Seschrock 	if (dp->spd_ctl_fill != NULL) {
476316Seschrock 		return (dp->spd_ctl_fill(sp, pp->ssp_page,
486316Seschrock 		    pp->ssp_len, np));
496316Seschrock 	} else {
506316Seschrock 		return (pp->ssp_page);
516316Seschrock 	}
526316Seschrock }
536316Seschrock 
546316Seschrock int
ses_fill_node(ses_node_t * np)556316Seschrock ses_fill_node(ses_node_t *np)
566316Seschrock {
576316Seschrock 	ses_target_t *tp = np->sn_snapshot->ss_target;
586316Seschrock 	ses_plugin_t *sp;
596316Seschrock 
606316Seschrock 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
616316Seschrock 		if (sp->sp_node_parse == NULL)
626316Seschrock 			continue;
636316Seschrock 
646316Seschrock 		if (sp->sp_node_parse(sp, np) != 0)
656316Seschrock 			return (-1);
666316Seschrock 	}
676316Seschrock 
686316Seschrock 	return (0);
696316Seschrock }
706316Seschrock 
716316Seschrock int
ses_node_ctl(ses_node_t * np,const char * op,nvlist_t * arg)726316Seschrock ses_node_ctl(ses_node_t *np, const char *op, nvlist_t *arg)
736316Seschrock {
746316Seschrock 	ses_target_t *tp = np->sn_snapshot->ss_target;
756316Seschrock 	ses_plugin_t *sp;
766316Seschrock 	nvlist_t *nvl;
776316Seschrock 	nvpair_t *nvp;
786316Seschrock 	int ret;
796316Seschrock 
806316Seschrock 	if (nvlist_dup(arg, &nvl, 0) != 0)
816316Seschrock 		return (ses_set_errno(ESES_NOMEM));
826316Seschrock 
836316Seschrock 	/*
846316Seschrock 	 * Technically we could get away with a per-snapshot lock while we fill
856316Seschrock 	 * the control page contents, but this doesn't take much time and we
866316Seschrock 	 * want actual control operations to be protected per-target, so we just
876316Seschrock 	 * take the target lock.
886316Seschrock 	 */
896316Seschrock 	(void) pthread_mutex_lock(&tp->st_lock);
906316Seschrock 
916316Seschrock 	/*
926316Seschrock 	 * We walk the list of plugins backwards, so that a product-specific
936316Seschrock 	 * plugin can rewrite the nvlist to control operations in terms of the
946316Seschrock 	 * standard mechanisms, if desired.
956316Seschrock 	 */
966316Seschrock 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
976316Seschrock 		if (sp->sp_node_ctl == NULL)
986316Seschrock 			continue;
996316Seschrock 
1006316Seschrock 		if (sp->sp_node_ctl(sp, np, op, nvl) != 0) {
1016316Seschrock 			nvlist_free(nvl);
1026316Seschrock 			(void) pthread_mutex_unlock(&tp->st_lock);
1036316Seschrock 			return (-1);
1046316Seschrock 		}
1056316Seschrock 	}
1066316Seschrock 
1076316Seschrock 	if ((nvp = nvlist_next_nvpair(nvl, NULL)) != NULL) {
1086316Seschrock 		(void) ses_error(ESES_NOTSUP, "property '%s' invalid for "
1096316Seschrock 		    "this node", nvpair_name(nvp));
1106316Seschrock 		nvlist_free(nvl);
1116316Seschrock 		(void) pthread_mutex_unlock(&tp->st_lock);
1126316Seschrock 		return (-1);
1136316Seschrock 	}
1146316Seschrock 
1156316Seschrock 	nvlist_free(nvl);
1166316Seschrock 
1176316Seschrock 	ret = ses_snap_do_ctl(np->sn_snapshot);
1186316Seschrock 	(void) pthread_mutex_unlock(&tp->st_lock);
1196316Seschrock 
1206316Seschrock 	return (ret);
1216316Seschrock }
1226316Seschrock 
1236316Seschrock /*ARGSUSED*/
1246316Seschrock void *
ses_plugin_page_lookup(ses_plugin_t * sp,ses_snap_t * snap,int pagenum,ses_node_t * np,size_t * lenp)1256316Seschrock ses_plugin_page_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
1266316Seschrock     ses_node_t *np, size_t *lenp)
1276316Seschrock {
1286316Seschrock 	ses_snap_page_t *pp;
1296316Seschrock 	ses_target_t *tp = sp->sp_target;
1306316Seschrock 	ses_pagedesc_t *dp;
1316316Seschrock 
1326316Seschrock 	if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_DIAG)) == NULL)
1336316Seschrock 		return (NULL);
1346316Seschrock 
1356316Seschrock 	if ((pp = ses_snap_find_page(snap, pagenum, B_FALSE)) == NULL)
1366316Seschrock 		return (NULL);
1376316Seschrock 
1386316Seschrock 	if (dp->spd_index != NULL) {
1396316Seschrock 		return (dp->spd_index(sp, np, pp->ssp_page, pp->ssp_len,
1406316Seschrock 		    lenp));
1416316Seschrock 	} else {
1426316Seschrock 		*lenp = pp->ssp_len;
1436316Seschrock 		return (pp->ssp_page);
1446316Seschrock 	}
1456316Seschrock }
1466316Seschrock 
1476316Seschrock ses_pagedesc_t *
ses_get_pagedesc(ses_target_t * tp,int pagenum,ses_pagetype_t type)1486316Seschrock ses_get_pagedesc(ses_target_t *tp, int pagenum, ses_pagetype_t type)
1496316Seschrock {
1506316Seschrock 	ses_plugin_t *sp;
1516316Seschrock 	ses_pagedesc_t *dp;
1526316Seschrock 
1536316Seschrock 	for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
1546316Seschrock 		if (sp->sp_pages == NULL)
1556316Seschrock 			continue;
1566316Seschrock 
1576316Seschrock 		for (dp = &sp->sp_pages[0]; dp->spd_pagenum != -1;
1586316Seschrock 		    dp++) {
1596316Seschrock 			if ((type == SES_PAGE_CTL && dp->spd_ctl_len == NULL) ||
1606316Seschrock 			    (type == SES_PAGE_DIAG && dp->spd_ctl_len != NULL))
1616316Seschrock 				continue;
1626316Seschrock 
1636316Seschrock 			if (dp->spd_pagenum == pagenum)
1646316Seschrock 				return (dp);
1656316Seschrock 		}
1666316Seschrock 	}
1676316Seschrock 
1686316Seschrock 	(void) ses_error(ESES_BAD_PAGE, "failed to find page 0x%x", pagenum);
1696316Seschrock 	return (NULL);
1706316Seschrock }
1716316Seschrock 
1726316Seschrock int
ses_plugin_register(ses_plugin_t * sp,int version,ses_plugin_config_t * scp)1736316Seschrock ses_plugin_register(ses_plugin_t *sp, int version, ses_plugin_config_t *scp)
1746316Seschrock {
1756316Seschrock 	if (version != LIBSES_PLUGIN_VERSION)
1766316Seschrock 		return (ses_set_errno(ESES_VERSION));
1776316Seschrock 
1786316Seschrock 	sp->sp_pages = scp->spc_pages;
1796316Seschrock 	sp->sp_node_parse = scp->spc_node_parse;
1806316Seschrock 	sp->sp_node_ctl = scp->spc_node_ctl;
1816316Seschrock 
1826316Seschrock 	return (0);
1836316Seschrock }
1846316Seschrock 
1856316Seschrock void
ses_plugin_setspecific(ses_plugin_t * sp,void * data)1866316Seschrock ses_plugin_setspecific(ses_plugin_t *sp, void *data)
1876316Seschrock {
1886316Seschrock 	sp->sp_data = data;
1896316Seschrock }
1906316Seschrock 
1916316Seschrock void *
ses_plugin_getspecific(ses_plugin_t * sp)1926316Seschrock ses_plugin_getspecific(ses_plugin_t *sp)
1936316Seschrock {
1946316Seschrock 	return (sp->sp_data);
1956316Seschrock }
1966316Seschrock 
1976316Seschrock static void
ses_plugin_cleanstr(char * s)1986316Seschrock ses_plugin_cleanstr(char *s)
1996316Seschrock {
2006316Seschrock 	while (*s != '\0') {
2016316Seschrock 		if (*s == ' ' || *s == '/')
2026316Seschrock 			*s = '-';
2036316Seschrock 		s++;
2046316Seschrock 	}
2056316Seschrock }
2066316Seschrock 
2076316Seschrock static void
ses_plugin_destroy(ses_plugin_t * sp)2086316Seschrock ses_plugin_destroy(ses_plugin_t *sp)
2096316Seschrock {
2106316Seschrock 	if (sp->sp_initialized && sp->sp_fini != NULL)
2116316Seschrock 		sp->sp_fini(sp);
2126316Seschrock 
2136643Seschrock 	if (ses_plugin_dlclose)
2146316Seschrock 		(void) dlclose(sp->sp_object);
2156316Seschrock 
2166316Seschrock 	ses_free(sp);
2176316Seschrock }
2186316Seschrock 
2196316Seschrock static int
ses_plugin_loadone(ses_target_t * tp,const char * path,uint32_t pass)2206316Seschrock ses_plugin_loadone(ses_target_t *tp, const char *path, uint32_t pass)
2216316Seschrock {
2226316Seschrock 	ses_plugin_t *sp, **loc;
2236316Seschrock 	void *obj;
2246316Seschrock 	int (*ses_priority)(void);
2256316Seschrock 
2266316Seschrock 	if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
2276316Seschrock 		return (0);
2286316Seschrock 
2296316Seschrock 	if ((sp = ses_zalloc(sizeof (ses_plugin_t))) == NULL) {
2306316Seschrock 		(void) dlclose(obj);
2316316Seschrock 		return (-1);
2326316Seschrock 	}
2336316Seschrock 
2346316Seschrock 	sp->sp_object = obj;
2356316Seschrock 	sp->sp_init = (int (*)())dlsym(obj, "_ses_init");
2366316Seschrock 	sp->sp_fini = (void (*)())dlsym(obj, "_ses_fini");
2376316Seschrock 	sp->sp_target = tp;
2386316Seschrock 
2396316Seschrock 	if (sp->sp_init == NULL) {
2406316Seschrock 		ses_plugin_destroy(sp);
2416316Seschrock 		return (0);
2426316Seschrock 	}
2436316Seschrock 
2446316Seschrock 	/*
2456316Seschrock 	 * Framework modules can establish an explicit prioritying by declaring
2466316Seschrock 	 * the '_ses_priority' symbol, which returns an integer used to create
2476316Seschrock 	 * an explicit ordering between plugins.
2486316Seschrock 	 */
2496316Seschrock 	if ((ses_priority = (int (*)())dlsym(obj, "_ses_priority")) != NULL)
2506316Seschrock 		sp->sp_priority = ses_priority();
2516316Seschrock 
2526316Seschrock 	sp->sp_priority |= (uint64_t)pass << 32;
2536316Seschrock 
2546316Seschrock 	for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) {
2556316Seschrock 		if ((*loc)->sp_priority > sp->sp_priority)
2566316Seschrock 			break;
2576316Seschrock 	}
2586316Seschrock 
2596316Seschrock 	if (*loc != NULL)
2606316Seschrock 		(*loc)->sp_prev = sp;
2616316Seschrock 	else
2626316Seschrock 		tp->st_plugin_last = sp;
2636316Seschrock 
2646316Seschrock 	sp->sp_next = *loc;
2656316Seschrock 	*loc = sp;
2666316Seschrock 
2676316Seschrock 	if (sp->sp_init(sp) != 0)
2686316Seschrock 		return (-1);
2696316Seschrock 	sp->sp_initialized = B_TRUE;
2706316Seschrock 
2716316Seschrock 	return (0);
2726316Seschrock }
2736316Seschrock 
2746316Seschrock static int
ses_plugin_load_dir(ses_target_t * tp,const char * pluginroot)2756316Seschrock ses_plugin_load_dir(ses_target_t *tp, const char *pluginroot)
2766316Seschrock {
2776316Seschrock 	char path[PATH_MAX];
2786316Seschrock 	DIR *dirp;
2796316Seschrock 	struct dirent64 *dp;
2806316Seschrock 	char *vendor, *product, *revision;
2816316Seschrock 	char isa[257];
2826316Seschrock 
2836316Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s",
2846316Seschrock 	    pluginroot, LIBSES_PLUGIN_FRAMEWORK);
2856316Seschrock 
2866316Seschrock #if defined(_LP64)
2876316Seschrock 	if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
2886316Seschrock 		isa[0] = '\0';
2896316Seschrock #else
2906316Seschrock 	isa[0] = '\0';
2916316Seschrock #endif
2926316Seschrock 
2936316Seschrock 	if ((dirp = opendir(path)) != NULL) {
2946316Seschrock 		while ((dp = readdir64(dirp)) != NULL) {
2956316Seschrock 			if (strcmp(dp->d_name, ".") == 0 ||
2966316Seschrock 			    strcmp(dp->d_name, "..") == 0)
2976316Seschrock 				continue;
2986316Seschrock 
2996316Seschrock 			(void) snprintf(path, sizeof (path), "%s/%s/%s/%s",
3006316Seschrock 			    pluginroot, LIBSES_PLUGIN_FRAMEWORK,
3016316Seschrock 			    isa, dp->d_name);
3026316Seschrock 
3036316Seschrock 			if (ses_plugin_loadone(tp, path, 0) != 0) {
3046316Seschrock 				(void) closedir(dirp);
3056316Seschrock 				return (-1);
3066316Seschrock 			}
3076316Seschrock 		}
3086316Seschrock 
3096316Seschrock 		(void) closedir(dirp);
3106316Seschrock 	}
3116316Seschrock 
3126316Seschrock 	/*
3136316Seschrock 	 * Create a local copy of the vendor/product/revision, strip out any
3146316Seschrock 	 * questionable characters, and then attempt to load each plugin.
3156316Seschrock 	 */
316*13093SRoger.Faulkner@Oracle.COM 	vendor = strdupa(libscsi_vendor(tp->st_target));
317*13093SRoger.Faulkner@Oracle.COM 	product = strdupa(libscsi_product(tp->st_target));
318*13093SRoger.Faulkner@Oracle.COM 	revision = strdupa(libscsi_revision(tp->st_target));
3196316Seschrock 
3206316Seschrock 	ses_plugin_cleanstr(vendor);
3216316Seschrock 	ses_plugin_cleanstr(product);
3226316Seschrock 	ses_plugin_cleanstr(revision);
3236316Seschrock 
3246316Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot,
3256316Seschrock 	    LIBSES_PLUGIN_VENDOR, isa, vendor,
3266316Seschrock 	    LIBSES_PLUGIN_EXT);
3276316Seschrock 	if (ses_plugin_loadone(tp, path, 1) != 0)
3286316Seschrock 		return (-1);
3296316Seschrock 
3306316Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot,
3316316Seschrock 	    LIBSES_PLUGIN_VENDOR, isa, vendor, product,
3326316Seschrock 	    LIBSES_PLUGIN_EXT);
3336316Seschrock 	if (ses_plugin_loadone(tp, path, 2) != 0)
3346316Seschrock 		return (-1);
3356316Seschrock 
3366316Seschrock 	(void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot,
3376316Seschrock 	    LIBSES_PLUGIN_VENDOR, isa, vendor, product,
3386316Seschrock 	    revision, LIBSES_PLUGIN_EXT);
3396316Seschrock 	if (ses_plugin_loadone(tp, path, 3) != 0)
3406316Seschrock 		return (-1);
3416316Seschrock 
3426316Seschrock 	return (0);
3436316Seschrock }
3446316Seschrock 
3456316Seschrock int
ses_plugin_load(ses_target_t * tp)3466316Seschrock ses_plugin_load(ses_target_t *tp)
3476316Seschrock {
3486316Seschrock 	char pluginroot[PATH_MAX];
3496316Seschrock 	const char *pluginpath, *p, *q;
3506316Seschrock 
3516316Seschrock 	if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL)
3526316Seschrock 		pluginpath = LIBSES_DEFAULT_PLUGINDIR;
3536316Seschrock 	ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL);
3546316Seschrock 
35512126SHyon.Kim@Sun.COM 	for (p = pluginpath; p != NULL; p = q) {
35612126SHyon.Kim@Sun.COM 		if ((q = strchr(p, ':')) != NULL) {
3576316Seschrock 			ptrdiff_t len = q - p;
3586316Seschrock 			(void) strncpy(pluginroot, p, len);
3596316Seschrock 			pluginroot[len] = '\0';
3606316Seschrock 			while (*q == ':')
3616316Seschrock 				++q;
3626316Seschrock 			if (*q == '\0')
3636316Seschrock 				q = NULL;
3646316Seschrock 			if (len == 0)
3656316Seschrock 				continue;
3666316Seschrock 		} else {
3676316Seschrock 			(void) strcpy(pluginroot, p);
3686316Seschrock 		}
3696316Seschrock 
3706316Seschrock 		if (pluginroot[0] != '/')
3716316Seschrock 			continue;
3726316Seschrock 
37312126SHyon.Kim@Sun.COM 		if (ses_plugin_load_dir(tp, pluginroot) != 0)
3746316Seschrock 			return (-1);
3756316Seschrock 	}
3766316Seschrock 
3776316Seschrock 	if (tp->st_plugin_first == NULL)
3786316Seschrock 		return (ses_error(ESES_PLUGIN, "no plugins found"));
3796316Seschrock 
3806316Seschrock 	return (0);
3816316Seschrock }
3826316Seschrock 
3836316Seschrock void
ses_plugin_unload(ses_target_t * tp)3846316Seschrock ses_plugin_unload(ses_target_t *tp)
3856316Seschrock {
3866316Seschrock 	ses_plugin_t *sp;
3876316Seschrock 
3886316Seschrock 	while ((sp = tp->st_plugin_first) != NULL) {
3896316Seschrock 		tp->st_plugin_first = sp->sp_next;
3906316Seschrock 		ses_plugin_destroy(sp);
3916316Seschrock 	}
3926316Seschrock }
393