xref: /illumos-gate/usr/src/uts/sun4v/io/platsvc.c (revision 24da5b34)
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 2007 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  * Power down 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 = ds_md_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 	if (ds_handle == DS_INVALID_HDL) {
233 		DBG("ps_md_data_handler: DS handle no longer valid\n");
234 		return;
235 	}
236 
237 	if (msg == NULL || buflen != sizeof (platsvc_md_update_req_t)) {
238 		resp_msg.req_num = 0;
239 		resp_msg.result = MD_UPDATE_INVALID_MSG;
240 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
241 		    sizeof (resp_msg))) != 0) {
242 			cmn_err(CE_NOTE, "md ds_cap_send failed (%d)", rv);
243 		}
244 		return;
245 	}
246 
247 	DBG("MD Reload...\n");
248 	if (mach_descrip_update()) {
249 		cmn_err(CE_WARN, "MD reload failed\n");
250 		return;
251 	}
252 
253 	recalc_xc_timeouts();
254 
255 	/*
256 	 * notify registered clients that MD has
257 	 * been updated
258 	 */
259 	mdeg_notify_clients();
260 
261 	resp_msg.req_num = msg->req_num;
262 	resp_msg.result = MD_UPDATE_SUCCESS;
263 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
264 		cmn_err(CE_NOTE, "md ds_cap_send resp failed (%d)", rv);
265 	}
266 }
267 
268 static void
269 ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
270 {
271 	ds_svc_hdl_t		ds_handle = ds_shutdown_handle;
272 	platsvc_shutdown_req_t	*msg = buf;
273 	platsvc_shutdown_resp_t	resp_msg;
274 	uint_t			rv;
275 	hrtime_t		start;
276 
277 	if (arg == NULL)
278 		return;
279 
280 	if (ds_handle == DS_INVALID_HDL) {
281 		DBG("ps_shutdown_data_handler: DS handle no longer valid\n");
282 		return;
283 	}
284 
285 	if (msg == NULL || buflen != sizeof (platsvc_shutdown_req_t)) {
286 		resp_msg.req_num = 0;
287 		resp_msg.result = DOMAIN_SHUTDOWN_INVALID_MSG;
288 		resp_msg.reason[0] = '\0';
289 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
290 		    sizeof (resp_msg))) != 0) {
291 			cmn_err(CE_NOTE, "shutdown ds_cap_send failed (%d)",
292 			    rv);
293 		}
294 		return;
295 	}
296 
297 	resp_msg.req_num = msg->req_num;
298 	resp_msg.result = DOMAIN_SHUTDOWN_SUCCESS;
299 	resp_msg.reason[0] = '\0';
300 
301 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
302 		cmn_err(CE_NOTE, "shutdown ds_cap_send resp failed (%d)", rv);
303 	}
304 
305 	/*
306 	 * Honor the ldoms manager's shutdown delay requirement.
307 	 */
308 	cmn_err(CE_NOTE, "shutdown requested by ldom manager, "
309 	    "system shutdown in %d minutes", MS2MIN(msg->delay));
310 
311 	start = gethrtime();
312 	while (gethrtime() - start < MS2NANO(msg->delay))
313 		;
314 
315 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
316 }
317 
318 
319 static void
320 ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
321 {
322 	ds_svc_hdl_t		ds_handle = ds_panic_handle;
323 	platsvc_panic_req_t	*msg = buf;
324 	platsvc_panic_resp_t	resp_msg;
325 	uint_t			rv;
326 
327 	if (arg == NULL)
328 		return;
329 
330 	if (ds_handle == DS_INVALID_HDL) {
331 		DBG("ps_panic_data_handler: DS handle no longer valid\n");
332 		return;
333 	}
334 
335 	if (msg == NULL || buflen != sizeof (platsvc_panic_req_t)) {
336 		resp_msg.req_num = 0;
337 		resp_msg.result = DOMAIN_PANIC_INVALID_MSG;
338 		resp_msg.reason[0] = '\0';
339 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
340 		    sizeof (resp_msg))) != 0) {
341 			cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)",
342 			    rv);
343 		}
344 		return;
345 	}
346 
347 	resp_msg.req_num = msg->req_num;
348 	resp_msg.result = DOMAIN_PANIC_SUCCESS;
349 	resp_msg.reason[0] = '\0';
350 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
351 		cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)", rv);
352 	}
353 
354 	cmn_err(CE_PANIC, "Panic forced by ldom manager");
355 	_NOTE(NOTREACHED)
356 }
357 
358 static void
359 ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
360 {
361 	DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
362 	    arg, ver->major, ver->minor, hdl);
363 
364 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
365 		ds_md_handle = hdl;
366 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
367 		ds_shutdown_handle = hdl;
368 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
369 		ds_panic_handle = hdl;
370 }
371 
372 static void
373 ps_unreg_handler(ds_cb_arg_t arg)
374 {
375 	DBG("ps_unreg_handler: arg=0x%p\n", arg);
376 
377 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
378 		ds_md_handle = DS_INVALID_HDL;
379 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
380 		ds_shutdown_handle = DS_INVALID_HDL;
381 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
382 		ds_panic_handle = DS_INVALID_HDL;
383 }
384