1275c9da8Seschrock /* 2275c9da8Seschrock * CDDL HEADER START 3275c9da8Seschrock * 4275c9da8Seschrock * The contents of this file are subject to the terms of the 5275c9da8Seschrock * Common Development and Distribution License (the "License"). 6275c9da8Seschrock * You may not use this file except in compliance with the License. 7275c9da8Seschrock * 8275c9da8Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9275c9da8Seschrock * or http://www.opensolaris.org/os/licensing. 10275c9da8Seschrock * See the License for the specific language governing permissions 11275c9da8Seschrock * and limitations under the License. 12275c9da8Seschrock * 13275c9da8Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14275c9da8Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15275c9da8Seschrock * If applicable, add the following below this CDDL HEADER, with the 16275c9da8Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17275c9da8Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18275c9da8Seschrock * 19275c9da8Seschrock * CDDL HEADER END 20275c9da8Seschrock */ 21275c9da8Seschrock 22275c9da8Seschrock /* 23ac88567aSHyon Kim * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24275c9da8Seschrock */ 25*a8be79faSAlexander Stetsenko /* 26*a8be79faSAlexander Stetsenko * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 27*a8be79faSAlexander Stetsenko */ 28275c9da8Seschrock 29275c9da8Seschrock #include <scsi/libses.h> 30275c9da8Seschrock #include "ses_impl.h" 31275c9da8Seschrock 32275c9da8Seschrock static boolean_t ses_plugin_dlclose; 33275c9da8Seschrock 34275c9da8Seschrock /*ARGSUSED*/ 35275c9da8Seschrock void * 36275c9da8Seschrock ses_plugin_ctlpage_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum, 37275c9da8Seschrock size_t len, ses_node_t *np, boolean_t unique) 38275c9da8Seschrock { 39275c9da8Seschrock ses_target_t *tp = snap->ss_target; 40275c9da8Seschrock ses_snap_page_t *pp; 41275c9da8Seschrock ses_pagedesc_t *dp; 42275c9da8Seschrock 43275c9da8Seschrock if ((pp = ses_snap_ctl_page(snap, pagenum, len, unique)) == NULL) 44275c9da8Seschrock return (NULL); 45275c9da8Seschrock 46275c9da8Seschrock if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_CTL)) == NULL) 47275c9da8Seschrock return (NULL); 48275c9da8Seschrock 49*a8be79faSAlexander Stetsenko if (np != NULL && dp->spd_ctl_fill != NULL) { 50275c9da8Seschrock return (dp->spd_ctl_fill(sp, pp->ssp_page, 51275c9da8Seschrock pp->ssp_len, np)); 52275c9da8Seschrock } else { 53275c9da8Seschrock return (pp->ssp_page); 54275c9da8Seschrock } 55275c9da8Seschrock } 56275c9da8Seschrock 57275c9da8Seschrock int 58275c9da8Seschrock ses_fill_node(ses_node_t *np) 59275c9da8Seschrock { 60275c9da8Seschrock ses_target_t *tp = np->sn_snapshot->ss_target; 61275c9da8Seschrock ses_plugin_t *sp; 62275c9da8Seschrock 63275c9da8Seschrock for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) { 64275c9da8Seschrock if (sp->sp_node_parse == NULL) 65275c9da8Seschrock continue; 66275c9da8Seschrock 67275c9da8Seschrock if (sp->sp_node_parse(sp, np) != 0) 68275c9da8Seschrock return (-1); 69275c9da8Seschrock } 70275c9da8Seschrock 71275c9da8Seschrock return (0); 72275c9da8Seschrock } 73275c9da8Seschrock 74275c9da8Seschrock int 75275c9da8Seschrock ses_node_ctl(ses_node_t *np, const char *op, nvlist_t *arg) 76275c9da8Seschrock { 77275c9da8Seschrock ses_target_t *tp = np->sn_snapshot->ss_target; 78275c9da8Seschrock ses_plugin_t *sp; 79275c9da8Seschrock nvlist_t *nvl; 80275c9da8Seschrock nvpair_t *nvp; 81275c9da8Seschrock int ret; 82275c9da8Seschrock 83275c9da8Seschrock if (nvlist_dup(arg, &nvl, 0) != 0) 84275c9da8Seschrock return (ses_set_errno(ESES_NOMEM)); 85275c9da8Seschrock 86275c9da8Seschrock /* 87275c9da8Seschrock * Technically we could get away with a per-snapshot lock while we fill 88275c9da8Seschrock * the control page contents, but this doesn't take much time and we 89275c9da8Seschrock * want actual control operations to be protected per-target, so we just 90275c9da8Seschrock * take the target lock. 91275c9da8Seschrock */ 92275c9da8Seschrock (void) pthread_mutex_lock(&tp->st_lock); 93275c9da8Seschrock 94275c9da8Seschrock /* 95275c9da8Seschrock * We walk the list of plugins backwards, so that a product-specific 96275c9da8Seschrock * plugin can rewrite the nvlist to control operations in terms of the 97275c9da8Seschrock * standard mechanisms, if desired. 98275c9da8Seschrock */ 99275c9da8Seschrock for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) { 100275c9da8Seschrock if (sp->sp_node_ctl == NULL) 101275c9da8Seschrock continue; 102275c9da8Seschrock 103275c9da8Seschrock if (sp->sp_node_ctl(sp, np, op, nvl) != 0) { 104275c9da8Seschrock nvlist_free(nvl); 105275c9da8Seschrock (void) pthread_mutex_unlock(&tp->st_lock); 106275c9da8Seschrock return (-1); 107275c9da8Seschrock } 108275c9da8Seschrock } 109275c9da8Seschrock 110275c9da8Seschrock if ((nvp = nvlist_next_nvpair(nvl, NULL)) != NULL) { 111275c9da8Seschrock (void) ses_error(ESES_NOTSUP, "property '%s' invalid for " 112275c9da8Seschrock "this node", nvpair_name(nvp)); 113275c9da8Seschrock nvlist_free(nvl); 114275c9da8Seschrock (void) pthread_mutex_unlock(&tp->st_lock); 115275c9da8Seschrock return (-1); 116275c9da8Seschrock } 117275c9da8Seschrock 118275c9da8Seschrock nvlist_free(nvl); 119275c9da8Seschrock 120275c9da8Seschrock ret = ses_snap_do_ctl(np->sn_snapshot); 121275c9da8Seschrock (void) pthread_mutex_unlock(&tp->st_lock); 122275c9da8Seschrock 123275c9da8Seschrock return (ret); 124275c9da8Seschrock } 125275c9da8Seschrock 126275c9da8Seschrock /*ARGSUSED*/ 127275c9da8Seschrock void * 128275c9da8Seschrock ses_plugin_page_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum, 129275c9da8Seschrock ses_node_t *np, size_t *lenp) 130275c9da8Seschrock { 131275c9da8Seschrock ses_snap_page_t *pp; 132275c9da8Seschrock ses_target_t *tp = sp->sp_target; 133275c9da8Seschrock ses_pagedesc_t *dp; 134275c9da8Seschrock 135275c9da8Seschrock if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_DIAG)) == NULL) 136275c9da8Seschrock return (NULL); 137275c9da8Seschrock 138275c9da8Seschrock if ((pp = ses_snap_find_page(snap, pagenum, B_FALSE)) == NULL) 139275c9da8Seschrock return (NULL); 140275c9da8Seschrock 141*a8be79faSAlexander Stetsenko if (np != NULL && dp->spd_index != NULL) { 142275c9da8Seschrock return (dp->spd_index(sp, np, pp->ssp_page, pp->ssp_len, 143275c9da8Seschrock lenp)); 144275c9da8Seschrock } else { 145275c9da8Seschrock *lenp = pp->ssp_len; 146275c9da8Seschrock return (pp->ssp_page); 147275c9da8Seschrock } 148275c9da8Seschrock } 149275c9da8Seschrock 150275c9da8Seschrock ses_pagedesc_t * 151275c9da8Seschrock ses_get_pagedesc(ses_target_t *tp, int pagenum, ses_pagetype_t type) 152275c9da8Seschrock { 153275c9da8Seschrock ses_plugin_t *sp; 154275c9da8Seschrock ses_pagedesc_t *dp; 155275c9da8Seschrock 156275c9da8Seschrock for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) { 157275c9da8Seschrock if (sp->sp_pages == NULL) 158275c9da8Seschrock continue; 159275c9da8Seschrock 160275c9da8Seschrock for (dp = &sp->sp_pages[0]; dp->spd_pagenum != -1; 161275c9da8Seschrock dp++) { 162275c9da8Seschrock if ((type == SES_PAGE_CTL && dp->spd_ctl_len == NULL) || 163275c9da8Seschrock (type == SES_PAGE_DIAG && dp->spd_ctl_len != NULL)) 164275c9da8Seschrock continue; 165275c9da8Seschrock 166275c9da8Seschrock if (dp->spd_pagenum == pagenum) 167275c9da8Seschrock return (dp); 168275c9da8Seschrock } 169275c9da8Seschrock } 170275c9da8Seschrock 171275c9da8Seschrock (void) ses_error(ESES_BAD_PAGE, "failed to find page 0x%x", pagenum); 172275c9da8Seschrock return (NULL); 173275c9da8Seschrock } 174275c9da8Seschrock 175275c9da8Seschrock int 176275c9da8Seschrock ses_plugin_register(ses_plugin_t *sp, int version, ses_plugin_config_t *scp) 177275c9da8Seschrock { 178275c9da8Seschrock if (version != LIBSES_PLUGIN_VERSION) 179275c9da8Seschrock return (ses_set_errno(ESES_VERSION)); 180275c9da8Seschrock 181275c9da8Seschrock sp->sp_pages = scp->spc_pages; 182275c9da8Seschrock sp->sp_node_parse = scp->spc_node_parse; 183275c9da8Seschrock sp->sp_node_ctl = scp->spc_node_ctl; 184275c9da8Seschrock 185275c9da8Seschrock return (0); 186275c9da8Seschrock } 187275c9da8Seschrock 188275c9da8Seschrock void 189275c9da8Seschrock ses_plugin_setspecific(ses_plugin_t *sp, void *data) 190275c9da8Seschrock { 191275c9da8Seschrock sp->sp_data = data; 192275c9da8Seschrock } 193275c9da8Seschrock 194275c9da8Seschrock void * 195275c9da8Seschrock ses_plugin_getspecific(ses_plugin_t *sp) 196275c9da8Seschrock { 197275c9da8Seschrock return (sp->sp_data); 198275c9da8Seschrock } 199275c9da8Seschrock 200275c9da8Seschrock static void 201275c9da8Seschrock ses_plugin_cleanstr(char *s) 202275c9da8Seschrock { 203275c9da8Seschrock while (*s != '\0') { 204275c9da8Seschrock if (*s == ' ' || *s == '/') 205275c9da8Seschrock *s = '-'; 206275c9da8Seschrock s++; 207275c9da8Seschrock } 208275c9da8Seschrock } 209275c9da8Seschrock 210275c9da8Seschrock static void 211275c9da8Seschrock ses_plugin_destroy(ses_plugin_t *sp) 212275c9da8Seschrock { 213275c9da8Seschrock if (sp->sp_initialized && sp->sp_fini != NULL) 214275c9da8Seschrock sp->sp_fini(sp); 215275c9da8Seschrock 216c5904d13Seschrock if (ses_plugin_dlclose) 217275c9da8Seschrock (void) dlclose(sp->sp_object); 218275c9da8Seschrock 219275c9da8Seschrock ses_free(sp); 220275c9da8Seschrock } 221275c9da8Seschrock 222275c9da8Seschrock static int 223275c9da8Seschrock ses_plugin_loadone(ses_target_t *tp, const char *path, uint32_t pass) 224275c9da8Seschrock { 225275c9da8Seschrock ses_plugin_t *sp, **loc; 226275c9da8Seschrock void *obj; 227275c9da8Seschrock int (*ses_priority)(void); 228275c9da8Seschrock 229275c9da8Seschrock if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL) 230275c9da8Seschrock return (0); 231275c9da8Seschrock 232275c9da8Seschrock if ((sp = ses_zalloc(sizeof (ses_plugin_t))) == NULL) { 233275c9da8Seschrock (void) dlclose(obj); 234275c9da8Seschrock return (-1); 235275c9da8Seschrock } 236275c9da8Seschrock 237275c9da8Seschrock sp->sp_object = obj; 238275c9da8Seschrock sp->sp_init = (int (*)())dlsym(obj, "_ses_init"); 239275c9da8Seschrock sp->sp_fini = (void (*)())dlsym(obj, "_ses_fini"); 240275c9da8Seschrock sp->sp_target = tp; 241275c9da8Seschrock 242275c9da8Seschrock if (sp->sp_init == NULL) { 243275c9da8Seschrock ses_plugin_destroy(sp); 244275c9da8Seschrock return (0); 245275c9da8Seschrock } 246275c9da8Seschrock 247275c9da8Seschrock /* 248275c9da8Seschrock * Framework modules can establish an explicit prioritying by declaring 249275c9da8Seschrock * the '_ses_priority' symbol, which returns an integer used to create 250275c9da8Seschrock * an explicit ordering between plugins. 251275c9da8Seschrock */ 252275c9da8Seschrock if ((ses_priority = (int (*)())dlsym(obj, "_ses_priority")) != NULL) 253275c9da8Seschrock sp->sp_priority = ses_priority(); 254275c9da8Seschrock 255275c9da8Seschrock sp->sp_priority |= (uint64_t)pass << 32; 256275c9da8Seschrock 257275c9da8Seschrock for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) { 258275c9da8Seschrock if ((*loc)->sp_priority > sp->sp_priority) 259275c9da8Seschrock break; 260275c9da8Seschrock } 261275c9da8Seschrock 262275c9da8Seschrock if (*loc != NULL) 263275c9da8Seschrock (*loc)->sp_prev = sp; 264275c9da8Seschrock else 265275c9da8Seschrock tp->st_plugin_last = sp; 266275c9da8Seschrock 267275c9da8Seschrock sp->sp_next = *loc; 268275c9da8Seschrock *loc = sp; 269275c9da8Seschrock 270275c9da8Seschrock if (sp->sp_init(sp) != 0) 271275c9da8Seschrock return (-1); 272275c9da8Seschrock sp->sp_initialized = B_TRUE; 273275c9da8Seschrock 274275c9da8Seschrock return (0); 275275c9da8Seschrock } 276275c9da8Seschrock 277275c9da8Seschrock static int 278275c9da8Seschrock ses_plugin_load_dir(ses_target_t *tp, const char *pluginroot) 279275c9da8Seschrock { 280275c9da8Seschrock char path[PATH_MAX]; 281275c9da8Seschrock DIR *dirp; 282275c9da8Seschrock struct dirent64 *dp; 283275c9da8Seschrock char *vendor, *product, *revision; 284275c9da8Seschrock char isa[257]; 285275c9da8Seschrock 286275c9da8Seschrock (void) snprintf(path, sizeof (path), "%s/%s", 287275c9da8Seschrock pluginroot, LIBSES_PLUGIN_FRAMEWORK); 288275c9da8Seschrock 289275c9da8Seschrock #if defined(_LP64) 290275c9da8Seschrock if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0) 291275c9da8Seschrock isa[0] = '\0'; 292275c9da8Seschrock #else 293275c9da8Seschrock isa[0] = '\0'; 294275c9da8Seschrock #endif 295275c9da8Seschrock 296275c9da8Seschrock if ((dirp = opendir(path)) != NULL) { 297275c9da8Seschrock while ((dp = readdir64(dirp)) != NULL) { 298275c9da8Seschrock if (strcmp(dp->d_name, ".") == 0 || 299275c9da8Seschrock strcmp(dp->d_name, "..") == 0) 300275c9da8Seschrock continue; 301275c9da8Seschrock 302275c9da8Seschrock (void) snprintf(path, sizeof (path), "%s/%s/%s/%s", 303275c9da8Seschrock pluginroot, LIBSES_PLUGIN_FRAMEWORK, 304275c9da8Seschrock isa, dp->d_name); 305275c9da8Seschrock 306275c9da8Seschrock if (ses_plugin_loadone(tp, path, 0) != 0) { 307275c9da8Seschrock (void) closedir(dirp); 308275c9da8Seschrock return (-1); 309275c9da8Seschrock } 310275c9da8Seschrock } 311275c9da8Seschrock 312275c9da8Seschrock (void) closedir(dirp); 313275c9da8Seschrock } 314275c9da8Seschrock 315275c9da8Seschrock /* 316275c9da8Seschrock * Create a local copy of the vendor/product/revision, strip out any 317275c9da8Seschrock * questionable characters, and then attempt to load each plugin. 318275c9da8Seschrock */ 31923a1cceaSRoger A. Faulkner vendor = strdupa(libscsi_vendor(tp->st_target)); 32023a1cceaSRoger A. Faulkner product = strdupa(libscsi_product(tp->st_target)); 32123a1cceaSRoger A. Faulkner revision = strdupa(libscsi_revision(tp->st_target)); 322275c9da8Seschrock 323275c9da8Seschrock ses_plugin_cleanstr(vendor); 324275c9da8Seschrock ses_plugin_cleanstr(product); 325275c9da8Seschrock ses_plugin_cleanstr(revision); 326275c9da8Seschrock 327275c9da8Seschrock (void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot, 328275c9da8Seschrock LIBSES_PLUGIN_VENDOR, isa, vendor, 329275c9da8Seschrock LIBSES_PLUGIN_EXT); 330275c9da8Seschrock if (ses_plugin_loadone(tp, path, 1) != 0) 331275c9da8Seschrock return (-1); 332275c9da8Seschrock 333275c9da8Seschrock (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot, 334275c9da8Seschrock LIBSES_PLUGIN_VENDOR, isa, vendor, product, 335275c9da8Seschrock LIBSES_PLUGIN_EXT); 336275c9da8Seschrock if (ses_plugin_loadone(tp, path, 2) != 0) 337275c9da8Seschrock return (-1); 338275c9da8Seschrock 339275c9da8Seschrock (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot, 340275c9da8Seschrock LIBSES_PLUGIN_VENDOR, isa, vendor, product, 341275c9da8Seschrock revision, LIBSES_PLUGIN_EXT); 342275c9da8Seschrock if (ses_plugin_loadone(tp, path, 3) != 0) 343275c9da8Seschrock return (-1); 344275c9da8Seschrock 345275c9da8Seschrock return (0); 346275c9da8Seschrock } 347275c9da8Seschrock 348275c9da8Seschrock int 349275c9da8Seschrock ses_plugin_load(ses_target_t *tp) 350275c9da8Seschrock { 351275c9da8Seschrock char pluginroot[PATH_MAX]; 352275c9da8Seschrock const char *pluginpath, *p, *q; 353275c9da8Seschrock 354275c9da8Seschrock if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL) 355275c9da8Seschrock pluginpath = LIBSES_DEFAULT_PLUGINDIR; 356275c9da8Seschrock ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL); 357275c9da8Seschrock 358ac88567aSHyon Kim for (p = pluginpath; p != NULL; p = q) { 359ac88567aSHyon Kim if ((q = strchr(p, ':')) != NULL) { 360275c9da8Seschrock ptrdiff_t len = q - p; 361275c9da8Seschrock (void) strncpy(pluginroot, p, len); 362275c9da8Seschrock pluginroot[len] = '\0'; 363275c9da8Seschrock while (*q == ':') 364275c9da8Seschrock ++q; 365275c9da8Seschrock if (*q == '\0') 366275c9da8Seschrock q = NULL; 367275c9da8Seschrock if (len == 0) 368275c9da8Seschrock continue; 369275c9da8Seschrock } else { 370275c9da8Seschrock (void) strcpy(pluginroot, p); 371275c9da8Seschrock } 372275c9da8Seschrock 373275c9da8Seschrock if (pluginroot[0] != '/') 374275c9da8Seschrock continue; 375275c9da8Seschrock 376ac88567aSHyon Kim if (ses_plugin_load_dir(tp, pluginroot) != 0) 377275c9da8Seschrock return (-1); 378275c9da8Seschrock } 379275c9da8Seschrock 380275c9da8Seschrock if (tp->st_plugin_first == NULL) 381275c9da8Seschrock return (ses_error(ESES_PLUGIN, "no plugins found")); 382275c9da8Seschrock 383275c9da8Seschrock return (0); 384275c9da8Seschrock } 385275c9da8Seschrock 386275c9da8Seschrock void 387275c9da8Seschrock ses_plugin_unload(ses_target_t *tp) 388275c9da8Seschrock { 389275c9da8Seschrock ses_plugin_t *sp; 390275c9da8Seschrock 391275c9da8Seschrock while ((sp = tp->st_plugin_first) != NULL) { 392275c9da8Seschrock tp->st_plugin_first = sp->sp_next; 393275c9da8Seschrock ses_plugin_destroy(sp); 394275c9da8Seschrock } 395275c9da8Seschrock } 396