static int cluster_register(rcm_handle_t *hdl) { int bootflags; if (cluster_SUNW_os_registered) return (RCM_SUCCESS); if (_cladm(CL_INITIALIZE, CL_GET_BOOTFLAG, &bootflags) != 0) { rcm_log_message(RCM_ERROR, gettext("unable to check cluster status\n")); return (RCM_FAILURE); } /* attempt to determine if we are in cluster mode */ if (bootflags & CLUSTER_BOOTED) { if (rcm_register_interest(hdl, SUNW_OS, 0, NULL) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, gettext("failed to register\n")); return (RCM_FAILURE); } else { cluster_SUNW_os_registered = 1; rcm_log_message(RCM_DEBUG, "registered " SUNW_OS "\n"); } } return (RCM_SUCCESS); }
/* * Converts a libdevinfo node into a /devices path. Caller must free results. */ static char * get_rsrcname(di_node_t dinode) { int len; char *rsrcname; char *devfspath; char name[MAXPATHLEN]; if ((devfspath = di_devfs_path(dinode)) == NULL) { rcm_log_message(RCM_ERROR, "MPXIO: resource has null path.\n"); return (NULL); } len = snprintf(name, sizeof (name), "/devices%s", devfspath); di_devfs_path_free(devfspath); if (len >= sizeof (name)) { rcm_log_message(RCM_ERROR, "MPXIO: resource path too long.\n"); return (NULL); } if ((rsrcname = strdup(name)) == NULL) rcm_log_message(RCM_ERROR, "MPXIO: failed to allocate resource name (%s).\n", strerror(errno)); return (rsrcname); }
/* * Generate reply event from resource registration information */ static void generate_reply_event(int error, rcm_info_t *info, nvlist_t **ret) { nvlist_t *nvl = NULL; rcm_info_t *tmp; char *buf = NULL; size_t buflen = 0; rcm_log_message(RCM_TRACE4, "generating reply event\n"); /* Allocate an empty nvlist */ if ((errno = nvlist_alloc(&nvl, 0, 0)) > 0) { rcm_log_message(RCM_ERROR, gettext("nvlist_alloc failed: %s\n"), strerror(errno)); rcmd_exit(errno); } /* Encode the result of the operation in the nvlist */ if (errno = nvlist_add_int32(nvl, RCM_RESULT, error)) { rcm_log_message(RCM_ERROR, gettext("nvlist_add(RESULT) failed: %s\n"), strerror(errno)); rcmd_exit(errno); } /* Go through the RCM info tuples, appending them all to the nvlist */ tmp = info; while (tmp) { if (tmp->info) { buf = NULL; buflen = 0; if (errno = nvlist_pack(tmp->info, &buf, &buflen, NV_ENCODE_NATIVE, 0)) { rcm_log_message(RCM_ERROR, gettext("nvlist_pack(INFO) failed: %s\n"), strerror(errno)); rcmd_exit(errno); } if (errno = nvlist_add_byte_array(nvl, RCM_RESULT_INFO, (uchar_t *)buf, buflen)) { rcm_log_message(RCM_ERROR, gettext("nvlist_add(INFO) failed: %s\n"), strerror(errno)); rcmd_exit(errno); } (void) free(buf); nvlist_free(tmp->info); } info = tmp->next; (void) free(tmp); tmp = info; } /* Return the nvlist (unpacked) in the return argument */ rcm_print_nvlist(nvl); *ret = nvl; }
/* * To return usage information, just lookup the PHCI in the cache and return * a string identifying that it's a PHCI and describing its cached MPxIO state. * Recurse with the cached list of disks if dependents are to be included. */ static int mpxio_getinfo(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, char **infostr, char **errstr, nvlist_t *props, rcm_info_t **infop) { size_t len; int rv = RCM_SUCCESS; char *buf = NULL; char **clients = NULL; phci_list_t *reg; char c; rcm_log_message(RCM_TRACE1, "MPXIO: getinfo(%s)\n", rsrc); *infostr = NULL; *errstr = NULL; (void) mutex_lock(&mpxio_lock); if ((reg = lookup_phci(rsrc)) == NULL) { *errstr = strdup(MPXIO_MSG_CACHEFAIL); (void) mutex_unlock(&mpxio_lock); return (RCM_FAILURE); } len = snprintf(&c, 1, MPXIO_MSG_USAGE, s_state(reg->phci.state)); buf = calloc(len + 1, sizeof (char)); if ((buf == NULL) || (snprintf(buf, len + 1, MPXIO_MSG_USAGE, s_state(reg->phci.state)) > len + 1)) { *infostr = strdup(MPXIO_MSG_USAGEUNKNOWN); *errstr = strdup(gettext("Cannot construct usage string.")); (void) mutex_unlock(&mpxio_lock); if (buf) free(buf); return (RCM_FAILURE); } *infostr = buf; if (flags & RCM_INCLUDE_DEPENDENT) { rcm_log_message(RCM_TRACE2, "MPXIO: getting clients\n"); if (get_affected_clients(hdl, rsrc, CMD_GETINFO, flags, &clients) < 0) { *errstr = strdup(gettext("Cannot lookup clients.")); (void) mutex_unlock(&mpxio_lock); return (RCM_FAILURE); } if (clients) { rv = rcm_get_info_list(hdl, clients, flags, infop); free(clients); } else { rcm_log_message(RCM_TRACE2, "MPXIO: none found\n"); } } (void) mutex_unlock(&mpxio_lock); return (rv); }
/* * Top level function for event service */ void event_service(void **data, size_t *datalen) { int cmd; int lerrno; int seq_num; nvlist_t *nvl; nvlist_t *ret; rcm_log_message(RCM_TRACE1, "received door operation\n"); /* Decode the data from the door into an unpacked nvlist */ if (data == NULL || datalen == NULL) { rcm_log_message(RCM_ERROR, "received null door argument\n"); return; } if (lerrno = nvlist_unpack(*data, *datalen, &nvl, 0)) { rcm_log_message(RCM_ERROR, "received bad door argument, %s\n", strerror(lerrno)); return; } /* Do nothing if the door is just being knocked on */ if (errno = nvlist_lookup_int32(nvl, RCM_CMD, &cmd)) { rcm_log_message(RCM_ERROR, "bad door argument (nvlist_lookup=%s)\n", strerror(errno)); nvlist_free(nvl); return; } if (cmd == CMD_KNOCK) { rcm_log_message(RCM_TRACE1, "door event was just a knock\n"); nvlist_free(nvl); *data = NULL; *datalen = 0; return; } /* * Go increment thread count. Before daemon is fully initialized, * the event processing blocks inside this function. */ seq_num = rcmd_thr_incr(cmd); process_event(cmd, seq_num, nvl, &ret); nvlist_free(nvl); assert(ret != NULL); /* * Decrement thread count */ rcmd_thr_decr(); out: *data = ret; *datalen = 0; }
static int pool_get_info(rcm_handle_t *hdl, char *rsrcname, id_t pid, uint_t flag, char **infop, char **errorp, nvlist_t *props, rcm_info_t **dependent_info) { rcm_log_message(RCM_TRACE1, "POOL: RCM get info: '%s'\n", rsrcname); if ((*infop = strdup(gettext("POOL: In use by pool(4) subsystem"))) == NULL) { rcm_log_message(RCM_ERROR, gettext("POOL: get info(%s) malloc " "failure\n"), rsrcname); *infop = NULL; *errorp = NULL; return (RCM_FAILURE); } return (RCM_SUCCESS); }
/* * common cleanup/exit functions to ensure releasing locks */ static void rcmd_cleanup(int status) { if (status == 0) { rcm_log_message(RCM_INFO, gettext("rcm_daemon normal exit\n")); } else { rcm_log_message(RCM_ERROR, gettext("rcm_daemon exit: errno = %d\n"), status); } if (hold_daemon_lock) { exit_daemon_lock(); } }
/* * During each register callback: totally rebuild the group list from a new * libdevinfo snapshot, and then update the registrants. */ static int mpxio_register(rcm_handle_t *hdl) { int nclients = 0; di_node_t devroot; rcm_log_message(RCM_TRACE1, "MPXIO: register()\n"); (void) mutex_lock(&mpxio_lock); /* Destroy the previous group list */ free_grouplist(); /* Get a current libdevinfo snapshot */ if ((devroot = di_init("/", DINFOCPYALL | DINFOPATH)) == DI_NODE_NIL) { rcm_log_message(RCM_ERROR, "MPXIO: libdevinfo initialization failed (%s).\n", strerror(errno)); (void) mutex_unlock(&mpxio_lock); return (RCM_FAILURE); } /* * First count the total number of clients. This'll be a useful * upper bound when allocating client arrays within each group. */ (void) di_walk_node(devroot, DI_WALK_CLDFIRST, &nclients, get_nclients); rcm_log_message(RCM_TRACE2, gettext("MPXIO: found %d clients.\n"), nclients); /* * Then walk the libdevinfo snapshot, building up the new group list * along the way. Pass in the total number of clients (from above) to * assist in group construction. */ (void) di_walk_node(devroot, DI_WALK_CLDFIRST, &nclients, build_groups); /* Now with a new group list constructed, refresh the registrants */ refresh_regs(hdl); /* Free the libdevinfo snapshot */ di_fini(devroot); (void) mutex_unlock(&mpxio_lock); return (0); }
/* * Destroy the cache and mutex lock when being unloaded. */ int rcm_mod_fini() { phci_list_t *reg; phci_list_t *next; rcm_log_message(RCM_TRACE1, "MPXIO: rcm_mod_fini()\n"); /* Free the cache of MPxIO group information */ free_grouplist(); /* Free the cache of registrants */ reg = reg_list; while (reg) { next = reg->next; free(reg->phci.path); free(reg); reg = next; } /* Destroy the mutex for locking the caches */ (void) mutex_destroy(&mpxio_lock); return (RCM_SUCCESS); }
/* * Return name and version number for mod_info. */ const char * rcm_mod_info() { rcm_log_message(RCM_TRACE1, "MPXIO: rcm_mod_info()\n"); return (gettext("RCM MPxIO module 1.6")); }
/* * Merges the client disks connected to a particular MPxIO group in with a * previous array of disk clients. The result is to adjust the 'nclients' * value with the new count of disks in the array, and to adjust the 'disks' * value to be a larger array of disks including its original contents along * with the current group's contents merged in. */ static int merge_clients(int *nclients, char ***clientsp, group_t *group) { int i; int old_nclients; char **clients_new; if (group->nclients) { old_nclients = *nclients; *nclients += group->nclients; clients_new = realloc(*clientsp, ((*nclients) + 1) * sizeof (char *)); if (clients_new == NULL) { rcm_log_message(RCM_ERROR, "MPXIO: cannot reallocate client array (%s).\n", strerror(errno)); return (-1); } for (i = old_nclients; i < (*nclients); i++) { /* * Don't allocate space for individual disks in the * merged list. Just make references to the previously * allocated strings in the group_t structs themselves. */ clients_new[i] = group->clients[i - old_nclients]; } clients_new[(*nclients)] = NULL; *clientsp = clients_new; } return (0); }
/* * When SIGHUP is received, reload modules at the next safe moment (when * there is no DR activity. */ void catch_sighup(void) { rcm_log_message(RCM_INFO, gettext("SIGHUP received, will exit when daemon is idle\n")); rcmd_thr_signal(); }
/* * Return the mod-ops vector for initialization. */ struct rcm_mod_ops * rcm_mod_init() { rcm_log_message(RCM_TRACE1, "MPXIO: rcm_mod_init()\n"); return (&mpxio_ops); }
/* * If clients are affected, then they are probably offline and we need to * propagate this removal notification to them. We can also remove the * cache entry for this PHCI. If that leaves its group empty, then the * group will be removed during the next register callback. */ static int mpxio_remove(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, char **errstr, rcm_info_t **infop) { char **clients; int rv = RCM_SUCCESS; rcm_log_message(RCM_TRACE1, "MPXIO: remove(%s)\n", rsrc); (void) mutex_lock(&mpxio_lock); if (get_affected_clients(hdl, rsrc, CMD_REMOVE, flags, &clients) < 0) { *errstr = strdup(gettext("Cannot lookup clients.")); (void) mutex_unlock(&mpxio_lock); return (RCM_FAILURE); } if (clients) { rv = rcm_notify_remove_list(hdl, clients, flags, infop); free(clients); } (void) mutex_unlock(&mpxio_lock); return (rv); }
/* * MPxIO has no policy against offlining. If disks will be affected, then * base the return value for this request on the results of offlining the * list of disks. Otherwise succeed. */ static int mpxio_offline(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, char **errstr, rcm_info_t **infop) { char **clients = NULL; int rv = RCM_SUCCESS; rcm_log_message(RCM_TRACE1, "MPXIO: offline(%s)\n", rsrc); (void) mutex_lock(&mpxio_lock); if (get_affected_clients(hdl, rsrc, CMD_OFFLINE, flags, &clients) < 0) { *errstr = strdup(gettext("Cannot lookup clients.")); (void) mutex_unlock(&mpxio_lock); return (RCM_FAILURE); } if (clients) { rv = rcm_request_offline_list(hdl, clients, flags, infop); if (rv != RCM_SUCCESS) *errstr = strdup(MPXIO_MSG_LASTPATH); free(clients); } (void) mutex_unlock(&mpxio_lock); return (rv); }
/* * Tests whether or not an operation on a specific PHCI resource would affect * the array of client devices attached to the PHCI's MPxIO group. * * Returns: 1 if clients would be affected, 0 if not. */ static int detect_client_change(rcm_handle_t *hdl, int cmd, int flags, group_t *group, char *rsrc) { int i; int state; /* * Perform a full set analysis on the set of redundant PHCIs. When * there are no unaffected and online PHCIs, then changing the state * of the named PHCI results in a client state change. */ for (i = 0; i < group->nphcis; i++) { /* Filter the named resource out of the analysis */ if (strcmp(group->phcis[i].path, rsrc) == 0) continue; /* * If we find a path that's in the ONLINE or STANDBY state * that would be left over in the system after completing * whatever DR or hotplugging operation is in progress, then * return a 0. */ if ((group->phcis[i].state == DI_PATH_STATE_ONLINE) || (group->phcis[i].state == DI_PATH_STATE_STANDBY)) { if (rcm_get_rsrcstate(hdl, group->phcis[i].path, &state) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, "MPXIO: Failed to query resource state\n"); continue; } rcm_log_message(RCM_TRACE2, "MPXIO: state of %s: %d\n", group->phcis[i].path, state); if (state == RCM_STATE_ONLINE) { return (0); } } } /* * The analysis above didn't find a redundant path to take over. So * report that the state of the client resources will change. */ return (1); }
static int pool_request_offline(rcm_handle_t *hdl, char *rsrcname, id_t pid, uint_t flag, char **reason, rcm_info_t **dependent_info) { rcm_log_message(RCM_TRACE1, "POOL: requesting offline for: %s\n", rsrcname); return (RCM_SUCCESS); }
/* * Nothing is implemented for suspend operations. */ static int mpxio_suspend(rcm_handle_t *hdl, char *rsrc, id_t id, timespec_t *interval, uint_t flags, char **errstr, rcm_info_t **infop) { rcm_log_message(RCM_TRACE1, "MPXIO: suspend(%s)\n", rsrc); return (RCM_SUCCESS); }
static int pool_notify_remove(rcm_handle_t *hdl, char *rsrcname, id_t pid, uint_t flag, char **reason, rcm_info_t **dependent_info) { rcm_log_message(RCM_TRACE1, "POOL: notifying removal of: %s\n", rsrcname); return (RCM_SUCCESS); }
/* * Nothing is implemented for resume operations. */ static int mpxio_resume(rcm_handle_t *hdl, char *rsrc, id_t id, uint_t flags, char **errstr, rcm_info_t **infop) { rcm_log_message(RCM_TRACE1, "MPXIO: resume(%s)\n", rsrc); return (RCM_SUCCESS); }
/* * When SIGUSR1 is received, exit the thread */ void catch_sigusr1(void) { rcm_log_message(RCM_DEBUG, "SIGUSR1 received in thread %d\n", thr_self()); cleanup_poll_thread(); thr_exit(NULL); }
/*ARGSUSED*/ static int cluster_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **errstr, rcm_info_t **dependent) { if ((*errstr = strdup(OS_REMOVE_ERR)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_FAILURE); }
static int pool_notify_capacity_change(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, nvlist_t *nvlist, char **info, rcm_info_t **dependent_info) { rcm_log_message(RCM_TRACE1, "POOL: notifying capacity change for: %s (flags: %d)\n", rsrcname, flags); return (RCM_SUCCESS); }
static int pool_unregister(rcm_handle_t *hdl) { int i; rcm_log_message(RCM_TRACE1, "Pools RCM un-registered\n"); if (registered) { registered--; for (i = 0; registrations[i].rsrc != NULL; i++) if (rcm_unregister_capacity(hdl, (char *)registrations[i].rsrc, 0) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, gettext("POOL: unregister capacity failed " "for '%s'\n"), registrations[i].rsrc); } } return (RCM_SUCCESS); }
static int pool_request_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *time, uint_t flags, char **reason, rcm_info_t **dependent_info) { rcm_log_message(RCM_TRACE1, "POOL: requesting suspend for: %s\n", rsrcname); return (RCM_SUCCESS); }
/*ARGSUSED*/ static int cluster_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval, uint_t flags, char **errstr, rcm_info_t **dependent) { if ((*errstr = strdup(OS_SUSPEND_ERR)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_FAILURE); }
static int pool_register(rcm_handle_t *hdl) { int i; rcm_log_message(RCM_TRACE1, "Registering Pools RCM module\n"); if (registered) return (RCM_SUCCESS); registered++; for (i = 0; registrations[i].rsrc != NULL; i++) { if (rcm_register_capacity(hdl, (char *)registrations[i].rsrc, 0, NULL) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, gettext("POOL: failed to register capacity " "change for '%s'\n"), registrations[i].rsrc); } } return (RCM_SUCCESS); }
/*ARGSUSED*/ static int cluster_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent) { assert(rsrcname != NULL && infostr != NULL); if ((*infostr = strdup(OS_USAGE)) == NULL) rcm_log_message(RCM_ERROR, gettext("strdup failure\n")); return (RCM_SUCCESS); }
/* * net_online() * * Online the previously offlined resource, and online its dependents. */ static int net_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **reason, rcm_info_t **dependent_reason) { assert(hd != NULL); assert(rsrc != NULL); assert(id == (id_t)0); rcm_log_message(RCM_TRACE1, _("NET: online(%s)\n"), rsrc); return (net_passthru(hd, NET_ONLINE, rsrc, flag, reason, dependent_reason, NULL)); }
static int cluster_unregister(rcm_handle_t *hdl) { if (cluster_SUNW_os_registered) { if (rcm_unregister_interest(hdl, SUNW_OS, 0) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, gettext("failed to unregister")); } cluster_SUNW_os_registered = 0; } return (RCM_SUCCESS); }