diff options
Diffstat (limited to 'usr/src/cmd/picl/plugins/sun4v/piclsbl/piclsbl.c')
| -rw-r--r-- | usr/src/cmd/picl/plugins/sun4v/piclsbl/piclsbl.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/usr/src/cmd/picl/plugins/sun4v/piclsbl/piclsbl.c b/usr/src/cmd/picl/plugins/sun4v/piclsbl/piclsbl.c new file mode 100644 index 0000000000..286c571049 --- /dev/null +++ b/usr/src/cmd/picl/plugins/sun4v/piclsbl/piclsbl.c @@ -0,0 +1,414 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * PICL Ontario platform plug-in to message the SC to light + * or extinguish the hdd 'OK2RM' ready-to-service light in + * the event of a soft unconfigure or configure, respectively. + */ + +#include <picl.h> +#include <picltree.h> +#include <picldefs.h> +#include <stdio.h> +#include <umem.h> +#include <unistd.h> +#include <libnvpair.h> +#include <strings.h> +#include <syslog.h> +#include <dlfcn.h> +#include <link.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/raidioctl.h> +#include <libpcp.h> +#include "piclsbl.h" + +#include "errno.h" + +#pragma init(piclsbl_register) + +static void *pcp_handle; + +static char hba_devctl[MAXPATHLEN]; + +static int (* pcp_init_ptr)(); +static int (* pcp_send_recv_ptr)(); +static int (* pcp_close_ptr)(); + +static int load_pcp_libs(void); +static void piclsbl_init(void); +static void piclsbl_fini(void); +static void piclsbl_register(void); +static void piclsbl_handler(const char *ename, const void *earg, + size_t size, void *cookie); + +static picld_plugin_reg_t piclsbl_reg = { + PICLD_PLUGIN_VERSION_1, + PICLD_PLUGIN_CRITICAL, + "piclsbl", + piclsbl_init, + piclsbl_fini +}; + +/* + * called from init to load the pcp library + */ +static int +load_pcp_libs() +{ + char pcp_dl_lib[80]; + + (void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s", + LIB_PCP_PATH, PCPLIB); + + /* load the library and set up function pointers */ + if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == (void *) NULL) + return (1); + + pcp_init_ptr = (int(*)())dlsym(pcp_handle, "pcp_init"); + pcp_close_ptr = (int(*)())dlsym(pcp_handle, "pcp_close"); + pcp_send_recv_ptr = (int(*)())dlsym(pcp_handle, "pcp_send_recv"); + + if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL || + pcp_close_ptr == NULL) + return (1); + + return (0); +} + +/* + * callback routine for ptree_walk_tree_by_class() + */ +static int +cb_find_disk(picl_nodehdl_t node, void *args) +{ + disk_lookup_t *lookup = (disk_lookup_t *)args; + int status = -1; + char *n; + char path[PICL_PROPNAMELEN_MAX]; + + status = ptree_get_propval_by_name(node, "Path", (void *)&path, + PICL_PROPNAMELEN_MAX); + if (status != PICL_SUCCESS) { + return (PICL_WALK_CONTINUE); + } + + if (strcmp(path, lookup->path) == 0) { + lookup->disk = node; + lookup->result = DISK_FOUND; + + /* store the HBA's device path for use in check_raid() */ + n = strstr(path, "/sd"); + strncpy(n, "\0", 1); + (void) snprintf(hba_devctl, MAXPATHLEN, "/devices%s:devctl", + path); + + return (PICL_WALK_TERMINATE); + } + + return (PICL_WALK_CONTINUE); +} + +/* + * check a target for RAID membership + */ +static int +check_raid(int target) +{ + raid_config_t config; + int fd; + int numvols; + int i; + int j; + + /* + * hba_devctl is set to the onboard hba, so it will + * always house any onboard RAID volumes + */ + if ((fd = open(hba_devctl, O_RDONLY)) < 0) { + syslog(LOG_ERR, "%s", strerror(errno)); + return (0); + } + + /* + * look up the RAID configurations for the onboard + * HBA and check target against all member targets + */ + if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) { + syslog(LOG_ERR, "%s", strerror(errno)); + (void) close(fd); + return (0); + } + + for (i = 0; i < numvols; i++) { + config.unitid = i; + if (ioctl(fd, RAID_GETCONFIG, &config)) { + syslog(LOG_ERR, "%s", strerror(errno)); + (void) close(fd); + return (0); + } + + for (j = 0; j < config.ndisks; j++) { + if (config.disk[j] == target) { + (void) close(fd); + return (1); + } + } + } + (void) close(fd); + return (0); +} + +/* + * Ontario SBL event handler, subscribed to: + * PICLEVENT_SYSEVENT_DEVICE_ADDED + * PICLEVENT_SYSEVENT_DEVICE_REMOVED + */ +static void +piclsbl_handler(const char *ename, const void *earg, size_t size, + void *cookie) +{ + char *devfs_path; + char hdd_location[PICL_PROPNAMELEN_MAX]; + nvlist_t *nvlp = NULL; + pcp_msg_t send_msg; + pcp_msg_t recv_msg; + pcp_sbl_req_t *req_ptr = NULL; + pcp_sbl_resp_t *resp_ptr = NULL; + int status = -1; + int target; + disk_lookup_t lookup; + int channel_fd; + + /* + * setup the request data to attach to the libpcp msg + */ + if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t), + UMEM_DEFAULT)) == NULL) + goto sbl_return; + + /* + * This plugin serves to enable or disable the blue RAS + * 'ok-to-remove' LED that is on each of the 4 disks on the + * Ontario. We catch the event via the picl handler, and + * if the event is DEVICE_ADDED for one of our onboard disks, + * then we'll be turning off the LED. Otherwise, if the event + * is DEVICE_REMOVED, then we turn it on. + */ + if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0) + req_ptr->sbl_action = PCP_SBL_DISABLE; + else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0) + req_ptr->sbl_action = PCP_SBL_ENABLE; + else + goto sbl_return; + + /* + * retrieve the device's physical path from the event payload + */ + if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) + goto sbl_return; + if (nvlist_lookup_string(nvlp, "devfs-path", &devfs_path)) + goto sbl_return; + + /* + * look for this disk in the picl tree, and if it's + * location indicates that it's one of our internal + * disks, then set sbl_id to incdicate which one. + * otherwise, return as it is not one of our disks. + */ + lookup.path = strdup(devfs_path); + lookup.disk = NULL; + lookup.result = DISK_NOT_FOUND; + + /* first, find the disk */ + status = ptree_walk_tree_by_class(root_node, "disk", (void *)&lookup, + cb_find_disk); + if (status != PICL_SUCCESS) + goto sbl_return; + + if (lookup.result == DISK_FOUND) { + /* now, lookup it's location in the node */ + status = ptree_get_propval_by_name(lookup.disk, "Location", + (void *)&hdd_location, PICL_PROPNAMELEN_MAX); + if (status != PICL_SUCCESS) { + syslog(LOG_ERR, "piclsbl: failed hdd discovery"); + goto sbl_return; + } + } + + /* + * Strip off the target from the NAC name. + * The disk NAC will always be HDD# + */ + if (strncmp(hdd_location, NAC_DISK_PREFIX, + strlen(NAC_DISK_PREFIX)) == 0) { + (void) sscanf(hdd_location, "%*3s%d", &req_ptr->sbl_id); + target = (int)req_ptr->sbl_id; + } else { + /* this is not one of the onboard disks */ + goto sbl_return; + } + + /* + * check the onboard RAID configuration for this disk. if it is + * a member of a RAID and is not the RAID itself, ignore the event + */ + if (check_raid(target)) + goto sbl_return; + + /* + * we have the information we need, init the platform channel. + * the platform channel driver will only allow one connection + * at a time on this socket. on the offchance that more than + * one event comes in, we'll retry to initialize this connection + * up to 3 times + */ + if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) < 0) { + /* failed to init; wait and retry up to 3 times */ + int s = PCPINIT_TIMEOUT; + int retries = 0; + while (++retries) { + (void) sleep(s); + if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) >= 0) + break; + else if (retries == 3) { + syslog(LOG_ERR, "piclsbl: ", + "SC channel initialization failed"); + goto sbl_return; + } + /* continue */ + } + } + + /* + * populate the message for libpcp + */ + send_msg.msg_type = PCP_SBL_CONTROL; + send_msg.sub_type = NULL; + send_msg.msg_len = sizeof (pcp_sbl_req_t); + send_msg.msg_data = (uint8_t *)req_ptr; + + /* + * send the request, receive the response + */ + if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg, + PCPCOMM_TIMEOUT) < 0) { + /* we either timed out or erred; either way try again */ + int s = PCPCOMM_TIMEOUT; + (void) sleep(s); + if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg, + PCPCOMM_TIMEOUT) < 0) { + syslog(LOG_ERR, "piclsbl: communication failure"); + goto sbl_return; + } + } + + /* + * validate that this data was meant for us + */ + if (recv_msg.msg_type != PCP_SBL_CONTROL_R) { + syslog(LOG_ERR, "piclsbl: unbound packet received"); + goto sbl_return; + } + + /* + * verify that the LED action has taken place + */ + resp_ptr = (pcp_sbl_resp_t *)recv_msg.msg_data; + if (resp_ptr->status == PCP_SBL_ERROR) { + syslog(LOG_ERR, "piclsbl: OK2RM LED action error"); + goto sbl_return; + } + + /* + * ensure the LED action taken is the one requested + */ + if ((req_ptr->sbl_action == PCP_SBL_DISABLE) && + (resp_ptr->sbl_state != SBL_STATE_OFF)) + syslog(LOG_ERR, "piclsbl: OK2RM LED not OFF after disk " + "configuration"); + else if ((req_ptr->sbl_action == PCP_SBL_ENABLE) && + (resp_ptr->sbl_state != SBL_STATE_ON)) + syslog(LOG_ERR, "piclsbl: OK2RM LED not ON after disk " + "unconfiguration"); + else if (resp_ptr->sbl_state == SBL_STATE_UNKNOWN) + syslog(LOG_ERR, "piclsbl: OK2RM LED set to unknown state"); + +sbl_return: + + (*pcp_close_ptr)(channel_fd); + if (req_ptr != NULL) + umem_free(req_ptr, sizeof (pcp_sbl_req_t)); + if (resp_ptr != NULL) + free(resp_ptr); + if (nvlp != NULL) + nvlist_free(nvlp); +} + +static void +piclsbl_init(void) +{ + /* retrieve the root node for lookups in the event handler */ + if ((ptree_get_root(&root_node)) != NULL) + return; + + /* load libpcp */ + if (load_pcp_libs()) { + syslog(LOG_ERR, "piclsbl: failed to load libpcp"); + syslog(LOG_ERR, "piclsbl: aborting"); + return; + } + + /* + * register piclsbl_handler for both "sysevent-device-added" and + * and for "sysevent-device-removed" PICL events + */ + (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, + piclsbl_handler, NULL); + (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, + piclsbl_handler, NULL); +} + +static void +piclsbl_fini(void) +{ + /* unregister the event handler */ + (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, + piclsbl_handler, NULL); + (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, + piclsbl_handler, NULL); +} + +static void +piclsbl_register(void) +{ + picld_plugin_register(&piclsbl_reg); +} |
