xref: /illumos-gate/usr/src/uts/sun4v/io/platsvc.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * sun4v Platform Services Module
31  */
32 
33 #include <sys/modctl.h>
34 #include <sys/cmn_err.h>
35 #include <sys/machsystm.h>
36 #include <sys/note.h>
37 #include <sys/uadmin.h>
38 #include <sys/ds.h>
39 #include <sys/platsvc.h>
40 
41 /*
42  * Debugging routines
43  */
44 #ifdef DEBUG
45 uint_t ps_debug = 0x0;
46 #define	DBG	if (ps_debug) printf
47 #else /* DEBUG */
48 #define	DBG	_NOTE(CONSTCOND) if (0) printf
49 #endif /* DEBUG */
50 
51 /*
52  * Time resolution conversions.
53  */
54 #define	MS2NANO(x)	((x) * MICROSEC)
55 #define	MS2SEC(x)	((x) / MILLISEC)
56 #define	MS2MIN(x)	(MS2SEC(x) / 60)
57 
58 /*
59  * Domains Services interaction
60  */
61 static ds_svc_hdl_t	ds_md_handle;
62 static ds_svc_hdl_t	ds_shutdown_handle;
63 static ds_svc_hdl_t	ds_panic_handle;
64 
65 static ds_ver_t		ps_vers[] = {{ 1, 0 }};
66 #define	PS_NVERS	(sizeof (ps_vers) / sizeof (ps_vers[0]))
67 
68 static ds_capability_t ps_md_cap = {
69 	"md-update",		/* svc_id */
70 	ps_vers,		/* vers */
71 	PS_NVERS		/* nvers */
72 };
73 
74 static ds_capability_t ps_shutdown_cap = {
75 	"domain-shutdown",	/* svc_id */
76 	ps_vers,		/* vers */
77 	PS_NVERS		/* nvers */
78 };
79 
80 static ds_capability_t ps_panic_cap = {
81 	"domain-panic",		/* svc_id */
82 	ps_vers,		/* vers */
83 	PS_NVERS		/* nvers */
84 };
85 
86 static void ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
87 static void ps_unreg_handler(ds_cb_arg_t arg);
88 
89 static void ps_md_data_handler(ds_cb_arg_t arg, void * buf, size_t buflen);
90 static void ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
91 static void ps_panic_data_handler(ds_cb_arg_t arg, void * buf, size_t buflen);
92 
93 static ds_clnt_ops_t ps_md_ops = {
94 	ps_reg_handler,			/* ds_reg_cb */
95 	ps_unreg_handler,		/* ds_unreg_cb */
96 	ps_md_data_handler,		/* ds_data_cb */
97 	&ds_md_handle			/* cb_arg */
98 };
99 
100 static ds_clnt_ops_t ps_shutdown_ops = {
101 	ps_reg_handler,			/* ds_reg_cb */
102 	ps_unreg_handler,		/* ds_unreg_cb */
103 	ps_shutdown_data_handler,	/* ds_data_cb */
104 	&ds_shutdown_handle		/* cb_arg */
105 };
106 
107 static ds_clnt_ops_t ps_panic_ops = {
108 	ps_reg_handler,			/* ds_reg_cb */
109 	ps_unreg_handler,		/* ds_unreg_cb */
110 	ps_panic_data_handler,		/* ds_data_cb */
111 	&ds_panic_handle		/* cb_arg */
112 };
113 
114 static int ps_init(void);
115 static void ps_fini(void);
116 
117 /*
118  * Powerdown timeout value of 5 minutes.
119  */
120 #define	PLATSVC_POWERDOWN_DELAY		1200
121 
122 static struct modlmisc modlmisc = {
123 	&mod_miscops,
124 	"sun4v Platform Services %I%"
125 };
126 
127 static struct modlinkage modlinkage = {
128 	MODREV_1,
129 	(void *)&modlmisc,
130 	NULL
131 };
132 
133 int
134 _init(void)
135 {
136 	int	rv;
137 
138 	if ((rv = ps_init()) != 0)
139 		return (rv);
140 
141 	if ((rv = mod_install(&modlinkage)) != 0)
142 		ps_fini();
143 
144 	return (rv);
145 }
146 
147 int
148 _info(struct modinfo *modinfop)
149 {
150 	return (mod_info(&modlinkage, modinfop));
151 }
152 
153 int platsvc_allow_unload;
154 
155 int
156 _fini(void)
157 {
158 	int	status;
159 
160 	if (platsvc_allow_unload == 0)
161 		return (EBUSY);
162 
163 	if ((status = mod_remove(&modlinkage)) == 0)
164 		ps_fini();
165 
166 	return (status);
167 }
168 
169 static int
170 ps_init(void)
171 {
172 	int	rv;
173 	extern int mdeg_init(void);
174 
175 	/* register with domain services framework */
176 	rv = ds_cap_init(&ps_md_cap, &ps_md_ops);
177 	if (rv != 0) {
178 		cmn_err(CE_WARN, "ds_cap_init md-update failed: %d", rv);
179 		return (rv);
180 	}
181 
182 	rv = ds_cap_init(&ps_shutdown_cap, &ps_shutdown_ops);
183 	if (rv != 0) {
184 		cmn_err(CE_WARN, "ds_cap_init domain-shutdown failed: %d", rv);
185 		(void) ds_cap_fini(&ps_md_cap);
186 		return (rv);
187 	}
188 
189 	rv = ds_cap_init(&ps_panic_cap, &ps_panic_ops);
190 	if (rv != 0) {
191 		cmn_err(CE_WARN, "ds_cap_init domain-panic failed: %d", rv);
192 		(void) ds_cap_fini(&ps_md_cap);
193 		(void) ds_cap_fini(&ps_shutdown_cap);
194 		return (rv);
195 	}
196 
197 	rv = mdeg_init();
198 
199 	return (rv);
200 }
201 
202 static void
203 ps_fini(void)
204 {
205 	extern void mdeg_fini(void);
206 
207 	/*
208 	 * Stop incoming requests from Zeus
209 	 */
210 	(void) ds_cap_fini(&ps_md_cap);
211 	(void) ds_cap_fini(&ps_shutdown_cap);
212 	(void) ds_cap_fini(&ps_panic_cap);
213 
214 	mdeg_fini();
215 }
216 
217 static void
218 ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
219 {
220 	extern int mach_descrip_update(void);
221 	extern void mdeg_notify_clients(void);
222 	extern void recalc_xc_timeouts(void);
223 
224 	ds_svc_hdl_t		 ds_handle;
225 	platsvc_md_update_req_t	 *msg = buf;
226 	platsvc_md_update_resp_t resp_msg;
227 	uint_t			 rv;
228 
229 	if (arg == NULL)
230 		return;
231 
232 	ds_handle = ds_md_handle;
233 
234 	if (msg == NULL || buflen != sizeof (platsvc_md_update_req_t)) {
235 		resp_msg.req_num = 0;
236 		resp_msg.result = MD_UPDATE_INVALID_MSG;
237 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
238 		    sizeof (resp_msg))) != 0) {
239 			cmn_err(CE_NOTE, "md ds_cap_send failed (%d)", rv);
240 		}
241 		return;
242 	}
243 
244 	DBG("MD Reload...\n");
245 	if (mach_descrip_update()) {
246 		cmn_err(CE_WARN, "MD reload failed\n");
247 		return;
248 	}
249 
250 	recalc_xc_timeouts();
251 
252 	/*
253 	 * notify registered clients that MD has
254 	 * been updated
255 	 */
256 	mdeg_notify_clients();
257 
258 	resp_msg.req_num = msg->req_num;
259 	resp_msg.result = MD_UPDATE_SUCCESS;
260 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
261 		cmn_err(CE_NOTE, "md ds_cap_send resp failed (%d)", rv);
262 	}
263 }
264 
265 static void
266 ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
267 {
268 	ds_svc_hdl_t		ds_handle;
269 	platsvc_shutdown_req_t	*msg = buf;
270 	platsvc_shutdown_resp_t	resp_msg;
271 	uint_t			rv;
272 	hrtime_t		start;
273 
274 	if (arg == NULL)
275 		return;
276 
277 	ds_handle = ds_shutdown_handle;
278 
279 	if (msg == NULL || buflen != sizeof (platsvc_shutdown_req_t)) {
280 		resp_msg.req_num = 0;
281 		resp_msg.result = DOMAIN_SHUTDOWN_INVALID_MSG;
282 		resp_msg.reason[0] = '\0';
283 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
284 		    sizeof (resp_msg))) != 0) {
285 			cmn_err(CE_NOTE, "shutdown ds_cap_send failed (%d)",
286 			    rv);
287 		}
288 		return;
289 	}
290 
291 	resp_msg.req_num = msg->req_num;
292 	resp_msg.result = DOMAIN_SHUTDOWN_SUCCESS;
293 	resp_msg.reason[0] = '\0';
294 
295 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
296 		cmn_err(CE_NOTE, "shutdown ds_cap_send resp failed (%d)", rv);
297 	}
298 
299 	/*
300 	 * Honor the ldoms manager's shutdown delay requirement.
301 	 */
302 	cmn_err(CE_NOTE, "shutdown requested by ldom manager, "
303 	    "system shutdown in %d minutes", MS2MIN(msg->delay));
304 
305 	start = gethrtime();
306 	while (gethrtime() - start < MS2NANO(msg->delay))
307 		;
308 
309 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
310 }
311 
312 
313 static void
314 ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
315 {
316 	ds_svc_hdl_t		ds_handle;
317 	platsvc_panic_req_t	*msg = buf;
318 	platsvc_panic_resp_t	resp_msg;
319 	uint_t			rv;
320 
321 	if (arg == NULL)
322 		return;
323 
324 	ds_handle = ds_panic_handle;
325 
326 	if (msg == NULL || buflen != sizeof (platsvc_panic_req_t)) {
327 		resp_msg.req_num = 0;
328 		resp_msg.result = DOMAIN_PANIC_INVALID_MSG;
329 		resp_msg.reason[0] = '\0';
330 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
331 		    sizeof (resp_msg))) != 0) {
332 			cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)",
333 			    rv);
334 		}
335 		return;
336 	}
337 
338 	resp_msg.req_num = msg->req_num;
339 	resp_msg.result = DOMAIN_PANIC_SUCCESS;
340 	resp_msg.reason[0] = '\0';
341 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
342 		cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)", rv);
343 	}
344 
345 	cmn_err(CE_PANIC, "Panic forced by ldom manager");
346 	_NOTE(NOTREACHED)
347 }
348 
349 static void
350 ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
351 {
352 	DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
353 	    arg, ver->major, ver->minor, hdl);
354 
355 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
356 		ds_md_handle = hdl;
357 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
358 		ds_shutdown_handle = hdl;
359 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
360 		ds_panic_handle = hdl;
361 }
362 
363 static void
364 ps_unreg_handler(ds_cb_arg_t arg)
365 {
366 	DBG("ps_unreg_handler: arg=0x%p\n", arg);
367 
368 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
369 		ds_md_handle = DS_INVALID_HDL;
370 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
371 		ds_shutdown_handle = DS_INVALID_HDL;
372 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
373 		ds_panic_handle = DS_INVALID_HDL;
374 }
375