dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / picl / plugins / sun4v / piclsbl / piclsbl.c
blobdb6a424070195fc05ac263885dc160623aaa7c82
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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.
31 * Erie platforms (T1000) do not have ok-to-remove LEDs
32 * so they do not need handlers for the SBL events.
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"
57 #include "errno.h"
59 #pragma init(piclsbl_register)
61 static void *pcp_handle;
63 static char hba_devctl[MAXPATHLEN];
65 static int (* pcp_init_ptr)();
66 static int (* pcp_send_recv_ptr)();
67 static int (* pcp_close_ptr)();
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);
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
85 * called from init to load the pcp library
87 static int
88 load_pcp_libs()
90 char pcp_dl_lib[80];
92 (void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s",
93 LIB_PCP_PATH, PCPLIB);
95 /* load the library and set up function pointers */
96 if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == NULL)
97 return (1);
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");
103 if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL ||
104 pcp_close_ptr == NULL)
105 return (1);
107 return (0);
111 * callback routine for ptree_walk_tree_by_class()
113 static int
114 cb_find_disk(picl_nodehdl_t node, void *args)
116 disk_lookup_t *lookup = (disk_lookup_t *)args;
117 int status = -1;
118 char *n;
119 char path[PICL_PROPNAMELEN_MAX];
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);
127 if (strcmp(path, lookup->path) == 0) {
128 lookup->disk = node;
129 lookup->result = DISK_FOUND;
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);
137 return (PICL_WALK_TERMINATE);
140 return (PICL_WALK_CONTINUE);
144 * check a target for RAID membership
146 static int
147 check_raid(int target)
149 raid_config_t config;
150 int fd;
151 int numvols;
152 int i;
153 int j;
156 * hba_devctl is set to the onboard hba, so it will
157 * always house any onboard RAID volumes
159 if ((fd = open(hba_devctl, O_RDONLY)) < 0) {
160 syslog(LOG_ERR, "%s", strerror(errno));
161 return (0);
165 * look up the RAID configurations for the onboard
166 * HBA and check target against all member targets
168 if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
169 syslog(LOG_ERR, "%s", strerror(errno));
170 (void) close(fd);
171 return (0);
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);
182 for (j = 0; j < config.ndisks; j++) {
183 if (config.disk[j] == target) {
184 (void) close(fd);
185 return (1);
189 (void) close(fd);
190 return (0);
194 * Ontario SBL event handler, subscribed to:
195 * PICLEVENT_SYSEVENT_DEVICE_ADDED
196 * PICLEVENT_SYSEVENT_DEVICE_REMOVED
198 static void
199 piclsbl_handler(const char *ename, const void *earg, size_t size,
200 void *cookie)
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;
215 * setup the request data to attach to the libpcp msg
217 if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t),
218 UMEM_DEFAULT)) == NULL)
219 goto sbl_return;
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.
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;
237 * retrieve the device's physical path from the event payload
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;
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.
250 lookup.path = strdup(devfs_path);
251 lookup.disk = NULL;
252 lookup.result = DISK_NOT_FOUND;
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;
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;
271 * Strip off the target from the NAC name.
272 * The disk NAC will always be HDD#
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;
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
287 if (check_raid(target))
288 goto sbl_return;
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
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;
310 /* continue */
315 * populate the message for libpcp
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;
323 * send the request, receive the response
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;
338 * validate that this data was meant for us
340 if (recv_msg.msg_type != PCP_SBL_CONTROL_R) {
341 syslog(LOG_ERR, "piclsbl: unbound packet received");
342 goto sbl_return;
346 * verify that the LED action has taken place
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;
355 * ensure the LED action taken is the one requested
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");
368 sbl_return:
370 (*pcp_close_ptr)(channel_fd);
371 if (req_ptr != NULL)
372 umem_free(req_ptr, sizeof (pcp_sbl_req_t));
373 free(resp_ptr);
374 nvlist_free(nvlp);
377 static void
378 piclsbl_init(void)
380 char platbuf[SYS_NMLN];
382 /* check for Erie platform name */
383 if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) &&
384 ((strcmp(platbuf, ERIE_PLATFORM) == 0) ||
385 (strcmp(platbuf, ERIE_PLATFORM2) == 0)))
386 return;
388 /* retrieve the root node for lookups in the event handler */
389 if ((ptree_get_root(&root_node)) != NULL)
390 return;
392 /* load libpcp */
393 if (load_pcp_libs()) {
394 syslog(LOG_ERR, "piclsbl: failed to load libpcp");
395 syslog(LOG_ERR, "piclsbl: aborting");
396 return;
400 * register piclsbl_handler for both "sysevent-device-added" and
401 * and for "sysevent-device-removed" PICL events
403 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
404 piclsbl_handler, NULL);
405 (void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
406 piclsbl_handler, NULL);
409 static void
410 piclsbl_fini(void)
412 /* unregister the event handler */
413 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
414 piclsbl_handler, NULL);
415 (void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
416 piclsbl_handler, NULL);
419 static void
420 piclsbl_register(void)
422 picld_plugin_register(&piclsbl_reg);