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