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
load_pcp_libs()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
cb_find_disk(picl_nodehdl_t node,void * args)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
check_raid(int target)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
piclsbl_handler(const char * ename,const void * earg,size_t size,void * cookie)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
piclsbl_init(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
piclsbl_fini(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
piclsbl_register(void)422 piclsbl_register(void)
423 {
424 picld_plugin_register(&piclsbl_reg);
425 }
426