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 nvlist_free(nvlp); 376 } 377 378 static void 379 piclsbl_init(void) 380 { 381 char platbuf[SYS_NMLN]; 382 383 /* check for Erie platform name */ 384 if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) && 385 ((strcmp(platbuf, ERIE_PLATFORM) == 0) || 386 (strcmp(platbuf, ERIE_PLATFORM2) == 0))) 387 return; 388 389 /* retrieve the root node for lookups in the event handler */ 390 if ((ptree_get_root(&root_node)) != NULL) 391 return; 392 393 /* load libpcp */ 394 if (load_pcp_libs()) { 395 syslog(LOG_ERR, "piclsbl: failed to load libpcp"); 396 syslog(LOG_ERR, "piclsbl: aborting"); 397 return; 398 } 399 400 /* 401 * register piclsbl_handler for both "sysevent-device-added" and 402 * and for "sysevent-device-removed" PICL events 403 */ 404 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, 405 piclsbl_handler, NULL); 406 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, 407 piclsbl_handler, NULL); 408 } 409 410 static void 411 piclsbl_fini(void) 412 { 413 /* unregister the event handler */ 414 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED, 415 piclsbl_handler, NULL); 416 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED, 417 piclsbl_handler, NULL); 418 } 419 420 static void 421 piclsbl_register(void) 422 { 423 picld_plugin_register(&piclsbl_reg); 424 } 425