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