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