Home | History | Annotate | Download | only in piclsbl
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  *  PICL Ontario platform plug-in to message the SC to light
     28  *  or extinguish the hdd 'OK2RM' ready-to-service light in
     29  *  the event of a soft unconfigure or configure, respectively.
     30  *
     31  *  Erie platforms (T1000) do not have ok-to-remove LEDs
     32  *  so they do not need handlers for the SBL events.
     33  */
     34 
     35 #include <picl.h>
     36 #include <picltree.h>
     37 #include <picldefs.h>
     38 #include <stdio.h>
     39 #include <umem.h>
     40 #include <unistd.h>
     41 #include <libnvpair.h>
     42 #include <strings.h>
     43 #include <syslog.h>
     44 #include <dlfcn.h>
     45 #include <link.h>
     46 #include <signal.h>
     47 #include <sys/types.h>
     48 #include <sys/stat.h>
     49 #include <fcntl.h>
     50 #include <sys/param.h>
     51 #include <sys/raidioctl.h>
     52 #include <sys/utsname.h>
     53 #include <sys/systeminfo.h>
     54 #include <libpcp.h>
     55 #include "piclsbl.h"
     56 
     57 #include "errno.h"
     58 
     59 #pragma init(piclsbl_register)
     60 
     61 static void *pcp_handle;
     62 
     63 static char hba_devctl[MAXPATHLEN];
     64 
     65 static int (* pcp_init_ptr)();
     66 static int (* pcp_send_recv_ptr)();
     67 static int (* pcp_close_ptr)();
     68 
     69 static int load_pcp_libs(void);
     70 static void piclsbl_init(void);
     71 static void piclsbl_fini(void);
     72 static void piclsbl_register(void);
     73 static void piclsbl_handler(const char *ename, const void *earg,
     74 				size_t size, void *cookie);
     75 
     76 static picld_plugin_reg_t piclsbl_reg = {
     77 	PICLD_PLUGIN_VERSION_1,
     78 	PICLD_PLUGIN_CRITICAL,
     79 	"piclsbl",
     80 	piclsbl_init,
     81 	piclsbl_fini
     82 };
     83 
     84 /*
     85  * called from init to load the pcp library
     86  */
     87 static int
     88 load_pcp_libs()
     89 {
     90 	char pcp_dl_lib[80];
     91 
     92 	(void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s",
     93 	    LIB_PCP_PATH, PCPLIB);
     94 
     95 	/* load the library and set up function pointers */
     96 	if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == (void *) NULL)
     97 		return (1);
     98 
     99 	pcp_init_ptr = (int(*)())dlsym(pcp_handle, "pcp_init");
    100 	pcp_close_ptr = (int(*)())dlsym(pcp_handle, "pcp_close");
    101 	pcp_send_recv_ptr = (int(*)())dlsym(pcp_handle, "pcp_send_recv");
    102 
    103 	if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL ||
    104 	    pcp_close_ptr == NULL)
    105 		return (1);
    106 
    107 	return (0);
    108 }
    109 
    110 /*
    111  * callback routine for ptree_walk_tree_by_class()
    112  */
    113 static int
    114 cb_find_disk(picl_nodehdl_t node, void *args)
    115 {
    116 	disk_lookup_t *lookup  = (disk_lookup_t *)args;
    117 	int status = -1;
    118 	char *n;
    119 	char path[PICL_PROPNAMELEN_MAX];
    120 
    121 	status = ptree_get_propval_by_name(node, "Path", (void *)&path,
    122 	    PICL_PROPNAMELEN_MAX);
    123 	if (status != PICL_SUCCESS) {
    124 		return (PICL_WALK_CONTINUE);
    125 	}
    126 
    127 	if (strcmp(path, lookup->path) == 0) {
    128 		lookup->disk = node;
    129 		lookup->result = DISK_FOUND;
    130 
    131 		/* store the HBA's device path for use in check_raid() */
    132 		n = strstr(path, "/sd");
    133 		strncpy(n, "\0", 1);
    134 		(void) snprintf(hba_devctl, MAXPATHLEN, "/devices%s:devctl",
    135 		    path);
    136 
    137 		return (PICL_WALK_TERMINATE);
    138 	}
    139 
    140 	return (PICL_WALK_CONTINUE);
    141 }
    142 
    143 /*
    144  * check a target for RAID membership
    145  */
    146 static int
    147 check_raid(int target)
    148 {
    149 	raid_config_t config;
    150 	int fd;
    151 	int numvols;
    152 	int i;
    153 	int j;
    154 
    155 	/*
    156 	 * hba_devctl is set to the onboard hba, so it will
    157 	 * always house any onboard RAID volumes
    158 	 */
    159 	if ((fd = open(hba_devctl, O_RDONLY)) < 0) {
    160 		syslog(LOG_ERR, "%s", strerror(errno));
    161 		return (0);
    162 	}
    163 
    164 	/*
    165 	 * look up the RAID configurations for the onboard
    166 	 * HBA and check target against all member targets
    167 	 */
    168 	if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
    169 		syslog(LOG_ERR, "%s", strerror(errno));
    170 		(void) close(fd);
    171 		return (0);
    172 	}
    173 
    174 	for (i = 0; i < numvols; i++) {
    175 		config.unitid = i;
    176 		if (ioctl(fd, RAID_GETCONFIG, &config)) {
    177 			syslog(LOG_ERR, "%s", strerror(errno));
    178 			(void) close(fd);
    179 			return (0);
    180 		}
    181 
    182 		for (j = 0; j < config.ndisks; j++) {
    183 			if (config.disk[j] == target) {
    184 				(void) close(fd);
    185 				return (1);
    186 			}
    187 		}
    188 	}
    189 	(void) close(fd);
    190 	return (0);
    191 }
    192 
    193 /*
    194  * Ontario SBL event handler, subscribed to:
    195  * 	PICLEVENT_SYSEVENT_DEVICE_ADDED
    196  * 	PICLEVENT_SYSEVENT_DEVICE_REMOVED
    197  */
    198 static void
    199 piclsbl_handler(const char *ename, const void *earg, size_t size,
    200 		void *cookie)
    201 {
    202 	char		*devfs_path;
    203 	char		hdd_location[PICL_PROPNAMELEN_MAX];
    204 	nvlist_t	*nvlp = NULL;
    205 	pcp_msg_t	send_msg;
    206 	pcp_msg_t	recv_msg;
    207 	pcp_sbl_req_t	*req_ptr = NULL;
    208 	pcp_sbl_resp_t	*resp_ptr = NULL;
    209 	int		status = -1;
    210 	int		target;
    211 	disk_lookup_t	lookup;
    212 	int		channel_fd;
    213 
    214 	/*
    215 	 * setup the request data to attach to the libpcp msg
    216 	 */
    217 	if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t),
    218 	    UMEM_DEFAULT)) == NULL)
    219 		goto sbl_return;
    220 
    221 	/*
    222 	 * This plugin serves to enable or disable the blue RAS
    223 	 * 'ok-to-remove' LED that is on each of the 4 disks on the
    224 	 * Ontario.  We catch the event via the picl handler, and
    225 	 * if the event is DEVICE_ADDED for one of our onboard disks,
    226 	 * then we'll be turning off the LED. Otherwise, if the event
    227 	 * is DEVICE_REMOVED, then we turn it on.
    228 	 */
    229 	if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0)
    230 		req_ptr->sbl_action = PCP_SBL_DISABLE;
    231 	else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0)
    232 		req_ptr->sbl_action = PCP_SBL_ENABLE;
    233 	else
    234 		goto sbl_return;
    235 
    236 	/*
    237 	 * retrieve the device's physical path from the event payload
    238 	 */
    239 	if (nvlist_unpack((char *)earg, size, &nvlp, NULL))
    240 		goto sbl_return;
    241 	if (nvlist_lookup_string(nvlp, "devfs-path", &devfs_path))
    242 		goto sbl_return;
    243 
    244 	/*
    245 	 * look for this disk in the picl tree, and if it's
    246 	 * location indicates that it's one of our internal
    247 	 * disks, then set sbl_id to incdicate which one.
    248 	 * otherwise, return as it is not one of our disks.
    249 	 */
    250 	lookup.path = strdup(devfs_path);
    251 	lookup.disk = NULL;
    252 	lookup.result = DISK_NOT_FOUND;
    253 
    254 	/* first, find the disk */
    255 	status = ptree_walk_tree_by_class(root_node, "disk", (void *)&lookup,
    256 	    cb_find_disk);
    257 	if (status != PICL_SUCCESS)
    258 		goto sbl_return;
    259 
    260 	if (lookup.result == DISK_FOUND) {
    261 		/* now, lookup it's location in the node */
    262 		status = ptree_get_propval_by_name(lookup.disk, "Location",
    263 		    (void *)&hdd_location, PICL_PROPNAMELEN_MAX);
    264 		if (status != PICL_SUCCESS) {
    265 			syslog(LOG_ERR, "piclsbl: failed hdd discovery");
    266 			goto sbl_return;
    267 		}
    268 	}
    269 
    270 	/*
    271 	 * Strip off the target from the NAC name.
    272 	 * The disk NAC will always be HDD#
    273 	 */
    274 	if (strncmp(hdd_location, NAC_DISK_PREFIX,
    275 	    strlen(NAC_DISK_PREFIX)) == 0) {
    276 		(void) sscanf(hdd_location, "%*3s%d", &req_ptr->sbl_id);
    277 		target = (int)req_ptr->sbl_id;
    278 	} else {
    279 		/* this is not one of the onboard disks */
    280 		goto sbl_return;
    281 	}
    282 
    283 	/*
    284 	 * check the onboard RAID configuration for this disk. if it is
    285 	 * a member of a RAID and is not the RAID itself, ignore the event
    286 	 */
    287 	if (check_raid(target))
    288 		goto sbl_return;
    289 
    290 	/*
    291 	 * we have the information we need, init the platform channel.
    292 	 * the platform channel driver will only allow one connection
    293 	 * at a time on this socket. on the offchance that more than
    294 	 * one event comes in, we'll retry to initialize this connection
    295 	 * up to 3 times
    296 	 */
    297 	if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) < 0) {
    298 		/* failed to init; wait and retry up to 3 times */
    299 		int s = PCPINIT_TIMEOUT;
    300 		int retries = 0;
    301 		while (++retries) {
    302 			(void) sleep(s);
    303 			if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) >= 0)
    304 				break;
    305 			else if (retries == 3) {
    306 				syslog(LOG_ERR, "piclsbl: ",
    307 				    "SC channel initialization failed");
    308 				goto sbl_return;
    309 			}
    310 			/* continue */
    311 		}
    312 	}
    313 
    314 	/*
    315 	 * populate the message for libpcp
    316 	 */
    317 	send_msg.msg_type = PCP_SBL_CONTROL;
    318 	send_msg.sub_type = NULL;
    319 	send_msg.msg_len = sizeof (pcp_sbl_req_t);
    320 	send_msg.msg_data = (uint8_t *)req_ptr;
    321 
    322 	/*
    323 	 * send the request, receive the response
    324 	 */
    325 	if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
    326 	    PCPCOMM_TIMEOUT) < 0) {
    327 		/* we either timed out or erred; either way try again */
    328 		int s = PCPCOMM_TIMEOUT;
    329 		(void) sleep(s);
    330 		if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
    331 		    PCPCOMM_TIMEOUT) < 0) {
    332 			syslog(LOG_ERR, "piclsbl: communication failure");
    333 			goto sbl_return;
    334 		}
    335 	}
    336 
    337 	/*
    338 	 * validate that this data was meant for us
    339 	 */
    340 	if (recv_msg.msg_type != PCP_SBL_CONTROL_R) {
    341 		syslog(LOG_ERR, "piclsbl: unbound packet received");
    342 		goto sbl_return;
    343 	}
    344 
    345 	/*
    346 	 * verify that the LED action has taken place
    347 	 */
    348 	resp_ptr = (pcp_sbl_resp_t *)recv_msg.msg_data;
    349 	if (resp_ptr->status == PCP_SBL_ERROR) {
    350 		syslog(LOG_ERR, "piclsbl: OK2RM LED action error");
    351 		goto sbl_return;
    352 	}
    353 
    354 	/*
    355 	 * ensure the LED action taken is the one requested
    356 	 */
    357 	if ((req_ptr->sbl_action == PCP_SBL_DISABLE) &&
    358 	    (resp_ptr->sbl_state != SBL_STATE_OFF))
    359 		syslog(LOG_ERR, "piclsbl: OK2RM LED not OFF after disk "
    360 		    "configuration");
    361 	else if ((req_ptr->sbl_action == PCP_SBL_ENABLE) &&
    362 	    (resp_ptr->sbl_state != SBL_STATE_ON))
    363 		syslog(LOG_ERR, "piclsbl: OK2RM LED not ON after disk "
    364 		    "unconfiguration");
    365 	else if (resp_ptr->sbl_state == SBL_STATE_UNKNOWN)
    366 		syslog(LOG_ERR, "piclsbl: OK2RM LED set to unknown state");
    367 
    368 sbl_return:
    369 
    370 	(*pcp_close_ptr)(channel_fd);
    371 	if (req_ptr != NULL)
    372 		umem_free(req_ptr, sizeof (pcp_sbl_req_t));
    373 	if (resp_ptr != NULL)
    374 		free(resp_ptr);
    375 	if (nvlp != NULL)
    376 		nvlist_free(nvlp);
    377 }
    378 
    379 static void
    380 piclsbl_init(void)
    381 {
    382 	char	platbuf[SYS_NMLN];
    383 
    384 	/* check for Erie platform name */
    385 	if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) &&
    386 	    ((strcmp(platbuf, ERIE_PLATFORM) == 0) ||
    387 	    (strcmp(platbuf, ERIE_PLATFORM2) == 0)))
    388 		return;
    389 
    390 	/* retrieve the root node for lookups in the event handler */
    391 	if ((ptree_get_root(&root_node)) != NULL)
    392 		return;
    393 
    394 	/* load libpcp */
    395 	if (load_pcp_libs()) {
    396 		syslog(LOG_ERR, "piclsbl: failed to load libpcp");
    397 		syslog(LOG_ERR, "piclsbl: aborting");
    398 		return;
    399 	}
    400 
    401 	/*
    402 	 * register piclsbl_handler for both "sysevent-device-added" and
    403 	 * and for "sysevent-device-removed" PICL events
    404 	 */
    405 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
    406 	    piclsbl_handler, NULL);
    407 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
    408 	    piclsbl_handler, NULL);
    409 }
    410 
    411 static void
    412 piclsbl_fini(void)
    413 {
    414 	/* unregister the event handler */
    415 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
    416 	    piclsbl_handler, NULL);
    417 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
    418 	    piclsbl_handler, NULL);
    419 }
    420 
    421 static void
    422 piclsbl_register(void)
    423 {
    424 	picld_plugin_register(&piclsbl_reg);
    425 }
    426