xref: /titanic_50/usr/src/uts/sun4v/io/platsvc.c (revision 023e71de9e5670cebc23dd51162833661d3d2d3b)
11ae08745Sheppo /*
21ae08745Sheppo  * CDDL HEADER START
31ae08745Sheppo  *
41ae08745Sheppo  * The contents of this file are subject to the terms of the
51ae08745Sheppo  * Common Development and Distribution License (the "License").
61ae08745Sheppo  * You may not use this file except in compliance with the License.
71ae08745Sheppo  *
81ae08745Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91ae08745Sheppo  * or http://www.opensolaris.org/os/licensing.
101ae08745Sheppo  * See the License for the specific language governing permissions
111ae08745Sheppo  * and limitations under the License.
121ae08745Sheppo  *
131ae08745Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
141ae08745Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151ae08745Sheppo  * If applicable, add the following below this CDDL HEADER, with the
161ae08745Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
171ae08745Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
181ae08745Sheppo  *
191ae08745Sheppo  * CDDL HEADER END
201ae08745Sheppo  */
211ae08745Sheppo 
221ae08745Sheppo /*
23*023e71deSHaik Aftandilian  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241ae08745Sheppo  * Use is subject to license terms.
251ae08745Sheppo  */
261ae08745Sheppo 
271ae08745Sheppo /*
281ae08745Sheppo  * sun4v Platform Services Module
291ae08745Sheppo  */
301ae08745Sheppo 
311ae08745Sheppo #include <sys/modctl.h>
321ae08745Sheppo #include <sys/cmn_err.h>
331ae08745Sheppo #include <sys/machsystm.h>
341ae08745Sheppo #include <sys/note.h>
351ae08745Sheppo #include <sys/uadmin.h>
361ae08745Sheppo #include <sys/ds.h>
371ae08745Sheppo #include <sys/platsvc.h>
38*023e71deSHaik Aftandilian #include <sys/ddi.h>
39*023e71deSHaik Aftandilian #include <sys/suspend.h>
40*023e71deSHaik Aftandilian #include <sys/proc.h>
41*023e71deSHaik Aftandilian #include <sys/disp.h>
421ae08745Sheppo 
431ae08745Sheppo /*
441ae08745Sheppo  * Debugging routines
451ae08745Sheppo  */
461ae08745Sheppo #ifdef DEBUG
471ae08745Sheppo uint_t ps_debug = 0x0;
481ae08745Sheppo #define	DBG	if (ps_debug) printf
491ae08745Sheppo #else /* DEBUG */
501ae08745Sheppo #define	DBG	_NOTE(CONSTCOND) if (0) printf
511ae08745Sheppo #endif /* DEBUG */
521ae08745Sheppo 
531ae08745Sheppo /*
541ae08745Sheppo  * Time resolution conversions.
551ae08745Sheppo  */
561ae08745Sheppo #define	MS2NANO(x)	((x) * MICROSEC)
571ae08745Sheppo #define	MS2SEC(x)	((x) / MILLISEC)
581ae08745Sheppo #define	MS2MIN(x)	(MS2SEC(x) / 60)
59*023e71deSHaik Aftandilian #define	SEC2HZ(x)	(drv_usectohz((x) * MICROSEC))
601ae08745Sheppo 
611ae08745Sheppo /*
621ae08745Sheppo  * Domains Services interaction
631ae08745Sheppo  */
641ae08745Sheppo static ds_svc_hdl_t	ds_md_handle;
651ae08745Sheppo static ds_svc_hdl_t	ds_shutdown_handle;
661ae08745Sheppo static ds_svc_hdl_t	ds_panic_handle;
67*023e71deSHaik Aftandilian static ds_svc_hdl_t	ds_suspend_handle;
681ae08745Sheppo 
691ae08745Sheppo static ds_ver_t		ps_vers[] = {{ 1, 0 }};
701ae08745Sheppo #define	PS_NVERS	(sizeof (ps_vers) / sizeof (ps_vers[0]))
711ae08745Sheppo 
721ae08745Sheppo static ds_capability_t ps_md_cap = {
731ae08745Sheppo 	"md-update",		/* svc_id */
741ae08745Sheppo 	ps_vers,		/* vers */
751ae08745Sheppo 	PS_NVERS		/* nvers */
761ae08745Sheppo };
771ae08745Sheppo 
781ae08745Sheppo static ds_capability_t ps_shutdown_cap = {
791ae08745Sheppo 	"domain-shutdown",	/* svc_id */
801ae08745Sheppo 	ps_vers,		/* vers */
811ae08745Sheppo 	PS_NVERS		/* nvers */
821ae08745Sheppo };
831ae08745Sheppo 
841ae08745Sheppo static ds_capability_t ps_panic_cap = {
851ae08745Sheppo 	"domain-panic",		/* svc_id */
861ae08745Sheppo 	ps_vers,		/* vers */
871ae08745Sheppo 	PS_NVERS		/* nvers */
881ae08745Sheppo };
891ae08745Sheppo 
90*023e71deSHaik Aftandilian static ds_capability_t ps_suspend_cap = {
91*023e71deSHaik Aftandilian 	"domain-suspend",	/* svc_id */
92*023e71deSHaik Aftandilian 	ps_vers,		/* vers */
93*023e71deSHaik Aftandilian 	PS_NVERS		/* nvers */
94*023e71deSHaik Aftandilian };
95*023e71deSHaik Aftandilian 
961ae08745Sheppo static void ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
971ae08745Sheppo static void ps_unreg_handler(ds_cb_arg_t arg);
981ae08745Sheppo 
991ae08745Sheppo static void ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1001ae08745Sheppo static void ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1011ae08745Sheppo static void ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
102*023e71deSHaik Aftandilian static void ps_suspend_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1031ae08745Sheppo 
1041ae08745Sheppo static ds_clnt_ops_t ps_md_ops = {
1051ae08745Sheppo 	ps_reg_handler,			/* ds_reg_cb */
1061ae08745Sheppo 	ps_unreg_handler,		/* ds_unreg_cb */
1071ae08745Sheppo 	ps_md_data_handler,		/* ds_data_cb */
1081ae08745Sheppo 	&ds_md_handle			/* cb_arg */
1091ae08745Sheppo };
1101ae08745Sheppo 
1111ae08745Sheppo static ds_clnt_ops_t ps_shutdown_ops = {
1121ae08745Sheppo 	ps_reg_handler,			/* ds_reg_cb */
1131ae08745Sheppo 	ps_unreg_handler,		/* ds_unreg_cb */
1141ae08745Sheppo 	ps_shutdown_data_handler,	/* ds_data_cb */
1151ae08745Sheppo 	&ds_shutdown_handle		/* cb_arg */
1161ae08745Sheppo };
1171ae08745Sheppo 
1181ae08745Sheppo static ds_clnt_ops_t ps_panic_ops = {
1191ae08745Sheppo 	ps_reg_handler,			/* ds_reg_cb */
1201ae08745Sheppo 	ps_unreg_handler,		/* ds_unreg_cb */
1211ae08745Sheppo 	ps_panic_data_handler,		/* ds_data_cb */
1221ae08745Sheppo 	&ds_panic_handle		/* cb_arg */
1231ae08745Sheppo };
1241ae08745Sheppo 
125*023e71deSHaik Aftandilian static ds_clnt_ops_t ps_suspend_ops = {
126*023e71deSHaik Aftandilian 	ps_reg_handler,			/* ds_reg_cb */
127*023e71deSHaik Aftandilian 	ps_unreg_handler,		/* ds_unreg_cb */
128*023e71deSHaik Aftandilian 	ps_suspend_data_handler,	/* ds_data_cb */
129*023e71deSHaik Aftandilian 	&ds_suspend_handle		/* cb_arg */
130*023e71deSHaik Aftandilian };
131*023e71deSHaik Aftandilian 
1321ae08745Sheppo static int ps_init(void);
1331ae08745Sheppo static void ps_fini(void);
1341ae08745Sheppo 
1351ae08745Sheppo /*
1361ae08745Sheppo  * Power down timeout value of 5 minutes.
1371ae08745Sheppo  */
1381ae08745Sheppo #define	PLATSVC_POWERDOWN_DELAY		1200
1391ae08745Sheppo 
140*023e71deSHaik Aftandilian /*
141*023e71deSHaik Aftandilian  * Set to true if OS suspend is supported. If OS suspend is not
142*023e71deSHaik Aftandilian  * supported, the suspend service will not be started.
143*023e71deSHaik Aftandilian  */
144*023e71deSHaik Aftandilian static boolean_t ps_suspend_enabled = B_FALSE;
145*023e71deSHaik Aftandilian 
146*023e71deSHaik Aftandilian /*
147*023e71deSHaik Aftandilian  * Suspend service request handling
148*023e71deSHaik Aftandilian  */
149*023e71deSHaik Aftandilian typedef struct ps_suspend_data {
150*023e71deSHaik Aftandilian 	void		*buf;
151*023e71deSHaik Aftandilian 	size_t		buflen;
152*023e71deSHaik Aftandilian } ps_suspend_data_t;
153*023e71deSHaik Aftandilian 
154*023e71deSHaik Aftandilian static kmutex_t ps_suspend_mutex;
155*023e71deSHaik Aftandilian static kcondvar_t ps_suspend_cv;
156*023e71deSHaik Aftandilian 
157*023e71deSHaik Aftandilian static ps_suspend_data_t *ps_suspend_data = NULL;
158*023e71deSHaik Aftandilian static boolean_t ps_suspend_thread_exit = B_FALSE;
159*023e71deSHaik Aftandilian static kthread_t *ps_suspend_thread = NULL;
160*023e71deSHaik Aftandilian 
161*023e71deSHaik Aftandilian static void ps_suspend_sequence(ps_suspend_data_t *data);
162*023e71deSHaik Aftandilian static void ps_suspend_thread_func(void);
163*023e71deSHaik Aftandilian 
164*023e71deSHaik Aftandilian /*
165*023e71deSHaik Aftandilian  * The DELAY timeout is the time (in seconds) to wait for the
166*023e71deSHaik Aftandilian  * suspend service to be re-registered after a suspend/resume
167*023e71deSHaik Aftandilian  * operation. The INTVAL time is the time (in seconds) to wait
168*023e71deSHaik Aftandilian  * between retry attempts when sending the post-suspend message
169*023e71deSHaik Aftandilian  * after a suspend/resume operation.
170*023e71deSHaik Aftandilian  */
171*023e71deSHaik Aftandilian #define	PLATSVC_SUSPEND_REREG_DELAY	60
172*023e71deSHaik Aftandilian #define	PLATSVC_SUSPEND_RETRY_INTVAL	1
173*023e71deSHaik Aftandilian static int ps_suspend_rereg_delay = PLATSVC_SUSPEND_REREG_DELAY;
174*023e71deSHaik Aftandilian static int ps_suspend_retry_intval = PLATSVC_SUSPEND_RETRY_INTVAL;
175*023e71deSHaik Aftandilian 
176*023e71deSHaik Aftandilian 
1771ae08745Sheppo static struct modlmisc modlmisc = {
1781ae08745Sheppo 	&mod_miscops,
179f500b196SRichard Bean 	"sun4v Platform Services"
1801ae08745Sheppo };
1811ae08745Sheppo 
1821ae08745Sheppo static struct modlinkage modlinkage = {
1831ae08745Sheppo 	MODREV_1,
1841ae08745Sheppo 	(void *)&modlmisc,
1851ae08745Sheppo 	NULL
1861ae08745Sheppo };
1871ae08745Sheppo 
1881ae08745Sheppo int
1891ae08745Sheppo _init(void)
1901ae08745Sheppo {
1911ae08745Sheppo 	int	rv;
1921ae08745Sheppo 
1931ae08745Sheppo 	if ((rv = ps_init()) != 0)
1941ae08745Sheppo 		return (rv);
1951ae08745Sheppo 
1961ae08745Sheppo 	if ((rv = mod_install(&modlinkage)) != 0)
1971ae08745Sheppo 		ps_fini();
1981ae08745Sheppo 
1991ae08745Sheppo 	return (rv);
2001ae08745Sheppo }
2011ae08745Sheppo 
2021ae08745Sheppo int
2031ae08745Sheppo _info(struct modinfo *modinfop)
2041ae08745Sheppo {
2051ae08745Sheppo 	return (mod_info(&modlinkage, modinfop));
2061ae08745Sheppo }
2071ae08745Sheppo 
2081ae08745Sheppo int platsvc_allow_unload;
2091ae08745Sheppo 
2101ae08745Sheppo int
2111ae08745Sheppo _fini(void)
2121ae08745Sheppo {
2131ae08745Sheppo 	int	status;
2141ae08745Sheppo 
2151ae08745Sheppo 	if (platsvc_allow_unload == 0)
2161ae08745Sheppo 		return (EBUSY);
2171ae08745Sheppo 
2181ae08745Sheppo 	if ((status = mod_remove(&modlinkage)) == 0)
2191ae08745Sheppo 		ps_fini();
2201ae08745Sheppo 
2211ae08745Sheppo 	return (status);
2221ae08745Sheppo }
2231ae08745Sheppo 
2241ae08745Sheppo static int
2251ae08745Sheppo ps_init(void)
2261ae08745Sheppo {
2271ae08745Sheppo 	int	rv;
2281ae08745Sheppo 	extern int mdeg_init(void);
229*023e71deSHaik Aftandilian 	extern void mdeg_fini(void);
2301ae08745Sheppo 
2311ae08745Sheppo 	/* register with domain services framework */
2321ae08745Sheppo 	rv = ds_cap_init(&ps_md_cap, &ps_md_ops);
2331ae08745Sheppo 	if (rv != 0) {
2341ae08745Sheppo 		cmn_err(CE_WARN, "ds_cap_init md-update failed: %d", rv);
2351ae08745Sheppo 		return (rv);
2361ae08745Sheppo 	}
2371ae08745Sheppo 
238*023e71deSHaik Aftandilian 	rv = mdeg_init();
239*023e71deSHaik Aftandilian 	if (rv != 0) {
240*023e71deSHaik Aftandilian 		(void) ds_cap_fini(&ps_md_cap);
241*023e71deSHaik Aftandilian 		return (rv);
242*023e71deSHaik Aftandilian 	}
243*023e71deSHaik Aftandilian 
2441ae08745Sheppo 	rv = ds_cap_init(&ps_shutdown_cap, &ps_shutdown_ops);
2451ae08745Sheppo 	if (rv != 0) {
2461ae08745Sheppo 		cmn_err(CE_WARN, "ds_cap_init domain-shutdown failed: %d", rv);
247*023e71deSHaik Aftandilian 		mdeg_fini();
2481ae08745Sheppo 		(void) ds_cap_fini(&ps_md_cap);
2491ae08745Sheppo 		return (rv);
2501ae08745Sheppo 	}
2511ae08745Sheppo 
2521ae08745Sheppo 	rv = ds_cap_init(&ps_panic_cap, &ps_panic_ops);
2531ae08745Sheppo 	if (rv != 0) {
2541ae08745Sheppo 		cmn_err(CE_WARN, "ds_cap_init domain-panic failed: %d", rv);
2551ae08745Sheppo 		(void) ds_cap_fini(&ps_md_cap);
256*023e71deSHaik Aftandilian 		mdeg_fini();
2571ae08745Sheppo 		(void) ds_cap_fini(&ps_shutdown_cap);
2581ae08745Sheppo 		return (rv);
2591ae08745Sheppo 	}
2601ae08745Sheppo 
261*023e71deSHaik Aftandilian 	ps_suspend_enabled = suspend_supported();
2621ae08745Sheppo 
263*023e71deSHaik Aftandilian 	if (ps_suspend_enabled) {
264*023e71deSHaik Aftandilian 		mutex_init(&ps_suspend_mutex, NULL, MUTEX_DEFAULT, NULL);
265*023e71deSHaik Aftandilian 		cv_init(&ps_suspend_cv, NULL, CV_DEFAULT, NULL);
266*023e71deSHaik Aftandilian 		ps_suspend_thread_exit = B_FALSE;
267*023e71deSHaik Aftandilian 
268*023e71deSHaik Aftandilian 		rv = ds_cap_init(&ps_suspend_cap, &ps_suspend_ops);
269*023e71deSHaik Aftandilian 		if (rv != 0) {
270*023e71deSHaik Aftandilian 			cmn_err(CE_WARN, "ds_cap_init domain-suspend failed: "
271*023e71deSHaik Aftandilian 			    "%d", rv);
272*023e71deSHaik Aftandilian 			(void) ds_cap_fini(&ps_md_cap);
273*023e71deSHaik Aftandilian 			mdeg_fini();
274*023e71deSHaik Aftandilian 			(void) ds_cap_fini(&ps_shutdown_cap);
275*023e71deSHaik Aftandilian 			(void) ds_cap_fini(&ps_panic_cap);
276*023e71deSHaik Aftandilian 			mutex_destroy(&ps_suspend_mutex);
277*023e71deSHaik Aftandilian 			cv_destroy(&ps_suspend_cv);
2781ae08745Sheppo 			return (rv);
2791ae08745Sheppo 		}
2801ae08745Sheppo 
281*023e71deSHaik Aftandilian 		ps_suspend_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
282*023e71deSHaik Aftandilian 		    ps_suspend_thread_func, NULL, 0, &p0, TS_RUN, minclsyspri);
283*023e71deSHaik Aftandilian 	}
284*023e71deSHaik Aftandilian 
285*023e71deSHaik Aftandilian 	return (0);
286*023e71deSHaik Aftandilian }
287*023e71deSHaik Aftandilian 
2881ae08745Sheppo static void
2891ae08745Sheppo ps_fini(void)
2901ae08745Sheppo {
2911ae08745Sheppo 	extern void mdeg_fini(void);
2921ae08745Sheppo 
2931ae08745Sheppo 	/*
2941ae08745Sheppo 	 * Stop incoming requests from Zeus
2951ae08745Sheppo 	 */
2961ae08745Sheppo 	(void) ds_cap_fini(&ps_md_cap);
2971ae08745Sheppo 	(void) ds_cap_fini(&ps_shutdown_cap);
2981ae08745Sheppo 	(void) ds_cap_fini(&ps_panic_cap);
2991ae08745Sheppo 
300*023e71deSHaik Aftandilian 	if (ps_suspend_enabled) {
301*023e71deSHaik Aftandilian 		(void) ds_cap_fini(&ps_suspend_cap);
302*023e71deSHaik Aftandilian 		if (ps_suspend_thread != NULL) {
303*023e71deSHaik Aftandilian 			mutex_enter(&ps_suspend_mutex);
304*023e71deSHaik Aftandilian 			ps_suspend_thread_exit = B_TRUE;
305*023e71deSHaik Aftandilian 			cv_signal(&ps_suspend_cv);
306*023e71deSHaik Aftandilian 			mutex_exit(&ps_suspend_mutex);
307*023e71deSHaik Aftandilian 
308*023e71deSHaik Aftandilian 			thread_join(ps_suspend_thread->t_did);
309*023e71deSHaik Aftandilian 			ps_suspend_thread = NULL;
310*023e71deSHaik Aftandilian 
311*023e71deSHaik Aftandilian 			mutex_destroy(&ps_suspend_mutex);
312*023e71deSHaik Aftandilian 			cv_destroy(&ps_suspend_cv);
313*023e71deSHaik Aftandilian 		}
314*023e71deSHaik Aftandilian 	}
315*023e71deSHaik Aftandilian 
3161ae08745Sheppo 	mdeg_fini();
3171ae08745Sheppo }
3181ae08745Sheppo 
3191ae08745Sheppo static void
3201ae08745Sheppo ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
3211ae08745Sheppo {
3221ae08745Sheppo 	extern int mach_descrip_update(void);
3231ae08745Sheppo 	extern void mdeg_notify_clients(void);
324f273041fSjm22469 	extern void recalc_xc_timeouts(void);
3251ae08745Sheppo 
3264e476149Srsmaeda 	ds_svc_hdl_t		 ds_handle = ds_md_handle;
3271ae08745Sheppo 	platsvc_md_update_req_t	 *msg = buf;
3281ae08745Sheppo 	platsvc_md_update_resp_t resp_msg;
3291ae08745Sheppo 	uint_t			 rv;
3301ae08745Sheppo 
3311ae08745Sheppo 	if (arg == NULL)
3321ae08745Sheppo 		return;
3331ae08745Sheppo 
3344e476149Srsmaeda 	if (ds_handle == DS_INVALID_HDL) {
3354e476149Srsmaeda 		DBG("ps_md_data_handler: DS handle no longer valid\n");
3364e476149Srsmaeda 		return;
3374e476149Srsmaeda 	}
3381ae08745Sheppo 
3391ae08745Sheppo 	if (msg == NULL || buflen != sizeof (platsvc_md_update_req_t)) {
3401ae08745Sheppo 		resp_msg.req_num = 0;
3411ae08745Sheppo 		resp_msg.result = MD_UPDATE_INVALID_MSG;
3421ae08745Sheppo 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
3431ae08745Sheppo 		    sizeof (resp_msg))) != 0) {
3441ae08745Sheppo 			cmn_err(CE_NOTE, "md ds_cap_send failed (%d)", rv);
3451ae08745Sheppo 		}
3461ae08745Sheppo 		return;
3471ae08745Sheppo 	}
3481ae08745Sheppo 
3491ae08745Sheppo 	DBG("MD Reload...\n");
3501ae08745Sheppo 	if (mach_descrip_update()) {
3511ae08745Sheppo 		cmn_err(CE_WARN, "MD reload failed\n");
3521ae08745Sheppo 		return;
3531ae08745Sheppo 	}
3541ae08745Sheppo 
355f273041fSjm22469 	recalc_xc_timeouts();
356f273041fSjm22469 
3571ae08745Sheppo 	/*
3581ae08745Sheppo 	 * notify registered clients that MD has
3591ae08745Sheppo 	 * been updated
3601ae08745Sheppo 	 */
3611ae08745Sheppo 	mdeg_notify_clients();
3621ae08745Sheppo 
3631ae08745Sheppo 	resp_msg.req_num = msg->req_num;
3641ae08745Sheppo 	resp_msg.result = MD_UPDATE_SUCCESS;
3651ae08745Sheppo 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
3661ae08745Sheppo 		cmn_err(CE_NOTE, "md ds_cap_send resp failed (%d)", rv);
3671ae08745Sheppo 	}
3681ae08745Sheppo }
3691ae08745Sheppo 
3701ae08745Sheppo static void
3711ae08745Sheppo ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
3721ae08745Sheppo {
3734e476149Srsmaeda 	ds_svc_hdl_t		ds_handle = ds_shutdown_handle;
3741ae08745Sheppo 	platsvc_shutdown_req_t	*msg = buf;
3751ae08745Sheppo 	platsvc_shutdown_resp_t	resp_msg;
3761ae08745Sheppo 	uint_t			rv;
3771ae08745Sheppo 	hrtime_t		start;
3781ae08745Sheppo 
3791ae08745Sheppo 	if (arg == NULL)
3801ae08745Sheppo 		return;
3811ae08745Sheppo 
3824e476149Srsmaeda 	if (ds_handle == DS_INVALID_HDL) {
3834e476149Srsmaeda 		DBG("ps_shutdown_data_handler: DS handle no longer valid\n");
3844e476149Srsmaeda 		return;
3854e476149Srsmaeda 	}
3861ae08745Sheppo 
3871ae08745Sheppo 	if (msg == NULL || buflen != sizeof (platsvc_shutdown_req_t)) {
3881ae08745Sheppo 		resp_msg.req_num = 0;
3891ae08745Sheppo 		resp_msg.result = DOMAIN_SHUTDOWN_INVALID_MSG;
3901ae08745Sheppo 		resp_msg.reason[0] = '\0';
3911ae08745Sheppo 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
3921ae08745Sheppo 		    sizeof (resp_msg))) != 0) {
3931ae08745Sheppo 			cmn_err(CE_NOTE, "shutdown ds_cap_send failed (%d)",
3941ae08745Sheppo 			    rv);
3951ae08745Sheppo 		}
3961ae08745Sheppo 		return;
3971ae08745Sheppo 	}
3981ae08745Sheppo 
3991ae08745Sheppo 	resp_msg.req_num = msg->req_num;
4001ae08745Sheppo 	resp_msg.result = DOMAIN_SHUTDOWN_SUCCESS;
4011ae08745Sheppo 	resp_msg.reason[0] = '\0';
4021ae08745Sheppo 
4031ae08745Sheppo 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
4041ae08745Sheppo 		cmn_err(CE_NOTE, "shutdown ds_cap_send resp failed (%d)", rv);
4051ae08745Sheppo 	}
4061ae08745Sheppo 
4071ae08745Sheppo 	/*
4081ae08745Sheppo 	 * Honor the ldoms manager's shutdown delay requirement.
4091ae08745Sheppo 	 */
4101ae08745Sheppo 	cmn_err(CE_NOTE, "shutdown requested by ldom manager, "
4111ae08745Sheppo 	    "system shutdown in %d minutes", MS2MIN(msg->delay));
4121ae08745Sheppo 
4131ae08745Sheppo 	start = gethrtime();
4141ae08745Sheppo 	while (gethrtime() - start < MS2NANO(msg->delay))
4151ae08745Sheppo 		;
4161ae08745Sheppo 
4171ae08745Sheppo 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
4181ae08745Sheppo }
4191ae08745Sheppo 
4201ae08745Sheppo 
4211ae08745Sheppo static void
4221ae08745Sheppo ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
4231ae08745Sheppo {
4244e476149Srsmaeda 	ds_svc_hdl_t		ds_handle = ds_panic_handle;
4251ae08745Sheppo 	platsvc_panic_req_t	*msg = buf;
4261ae08745Sheppo 	platsvc_panic_resp_t	resp_msg;
4271ae08745Sheppo 	uint_t			rv;
4281ae08745Sheppo 
4291ae08745Sheppo 	if (arg == NULL)
4301ae08745Sheppo 		return;
4311ae08745Sheppo 
4324e476149Srsmaeda 	if (ds_handle == DS_INVALID_HDL) {
4334e476149Srsmaeda 		DBG("ps_panic_data_handler: DS handle no longer valid\n");
4344e476149Srsmaeda 		return;
4354e476149Srsmaeda 	}
4361ae08745Sheppo 
4371ae08745Sheppo 	if (msg == NULL || buflen != sizeof (platsvc_panic_req_t)) {
4381ae08745Sheppo 		resp_msg.req_num = 0;
4391ae08745Sheppo 		resp_msg.result = DOMAIN_PANIC_INVALID_MSG;
4401ae08745Sheppo 		resp_msg.reason[0] = '\0';
4411ae08745Sheppo 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
4421ae08745Sheppo 		    sizeof (resp_msg))) != 0) {
4431ae08745Sheppo 			cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)",
4441ae08745Sheppo 			    rv);
4451ae08745Sheppo 		}
4461ae08745Sheppo 		return;
4471ae08745Sheppo 	}
4481ae08745Sheppo 
4491ae08745Sheppo 	resp_msg.req_num = msg->req_num;
4501ae08745Sheppo 	resp_msg.result = DOMAIN_PANIC_SUCCESS;
4511ae08745Sheppo 	resp_msg.reason[0] = '\0';
4521ae08745Sheppo 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
4531ae08745Sheppo 		cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)", rv);
4541ae08745Sheppo 	}
4551ae08745Sheppo 
4561ae08745Sheppo 	cmn_err(CE_PANIC, "Panic forced by ldom manager");
4571ae08745Sheppo 	_NOTE(NOTREACHED)
4581ae08745Sheppo }
4591ae08745Sheppo 
460*023e71deSHaik Aftandilian /*
461*023e71deSHaik Aftandilian  * Send a suspend response message. If a timeout is specified, wait
462*023e71deSHaik Aftandilian  * intval seconds between attempts to send the message. The timeout
463*023e71deSHaik Aftandilian  * and intval arguments are in seconds.
464*023e71deSHaik Aftandilian  */
465*023e71deSHaik Aftandilian static void
466*023e71deSHaik Aftandilian ps_suspend_send_response(ds_svc_hdl_t *ds_handle, uint64_t req_num,
467*023e71deSHaik Aftandilian     uint32_t result, uint32_t rec_result, char *reason, int timeout,
468*023e71deSHaik Aftandilian     int intval)
469*023e71deSHaik Aftandilian {
470*023e71deSHaik Aftandilian 	platsvc_suspend_resp_t	*resp;
471*023e71deSHaik Aftandilian 	size_t			reason_length;
472*023e71deSHaik Aftandilian 	int			tries = 0;
473*023e71deSHaik Aftandilian 	int			rv = -1;
474*023e71deSHaik Aftandilian 	time_t			deadline;
475*023e71deSHaik Aftandilian 
476*023e71deSHaik Aftandilian 	if (reason == NULL) {
477*023e71deSHaik Aftandilian 		reason_length = 0;
478*023e71deSHaik Aftandilian 	} else {
479*023e71deSHaik Aftandilian 		/* Get number of non-NULL bytes */
480*023e71deSHaik Aftandilian 		reason_length = strnlen(reason, SUSPEND_MAX_REASON_SIZE - 1);
481*023e71deSHaik Aftandilian 		ASSERT(reason[reason_length] == '\0');
482*023e71deSHaik Aftandilian 		/* Account for NULL terminator */
483*023e71deSHaik Aftandilian 		reason_length++;
484*023e71deSHaik Aftandilian 	}
485*023e71deSHaik Aftandilian 
486*023e71deSHaik Aftandilian 	resp = (platsvc_suspend_resp_t *)
487*023e71deSHaik Aftandilian 	    kmem_zalloc(sizeof (platsvc_suspend_resp_t) + reason_length,
488*023e71deSHaik Aftandilian 	    KM_SLEEP);
489*023e71deSHaik Aftandilian 
490*023e71deSHaik Aftandilian 	resp->req_num = req_num;
491*023e71deSHaik Aftandilian 	resp->result = result;
492*023e71deSHaik Aftandilian 	resp->rec_result = rec_result;
493*023e71deSHaik Aftandilian 	if (reason_length > 0) {
494*023e71deSHaik Aftandilian 		bcopy(reason, &resp->reason, reason_length - 1);
495*023e71deSHaik Aftandilian 		/* Ensure NULL terminator is present */
496*023e71deSHaik Aftandilian 		resp->reason[reason_length] = '\0';
497*023e71deSHaik Aftandilian 	}
498*023e71deSHaik Aftandilian 
499*023e71deSHaik Aftandilian 	if (timeout == 0) {
500*023e71deSHaik Aftandilian 		tries++;
501*023e71deSHaik Aftandilian 		rv = ds_cap_send(*ds_handle, resp,
502*023e71deSHaik Aftandilian 		    sizeof (platsvc_suspend_resp_t) + reason_length);
503*023e71deSHaik Aftandilian 	} else {
504*023e71deSHaik Aftandilian 		deadline = gethrestime_sec() + timeout;
505*023e71deSHaik Aftandilian 		do {
506*023e71deSHaik Aftandilian 			ds_svc_hdl_t hdl;
507*023e71deSHaik Aftandilian 			/*
508*023e71deSHaik Aftandilian 			 * Copy the handle so we can ensure we never pass
509*023e71deSHaik Aftandilian 			 * an invalid handle to ds_cap_send. We don't want
510*023e71deSHaik Aftandilian 			 * to trigger warning messages just because the
511*023e71deSHaik Aftandilian 			 * service was temporarily unregistered.
512*023e71deSHaik Aftandilian 			 */
513*023e71deSHaik Aftandilian 			if ((hdl = *ds_handle) == DS_INVALID_HDL) {
514*023e71deSHaik Aftandilian 				delay(SEC2HZ(intval));
515*023e71deSHaik Aftandilian 			} else if ((rv = ds_cap_send(hdl, resp,
516*023e71deSHaik Aftandilian 			    sizeof (platsvc_suspend_resp_t) +
517*023e71deSHaik Aftandilian 			    reason_length)) != 0) {
518*023e71deSHaik Aftandilian 				tries++;
519*023e71deSHaik Aftandilian 				delay(SEC2HZ(intval));
520*023e71deSHaik Aftandilian 			}
521*023e71deSHaik Aftandilian 		} while ((rv != 0) && (gethrestime_sec() < deadline));
522*023e71deSHaik Aftandilian 	}
523*023e71deSHaik Aftandilian 
524*023e71deSHaik Aftandilian 	if (rv != 0) {
525*023e71deSHaik Aftandilian 		cmn_err(CE_NOTE, "suspend ds_cap_send resp failed (%d) "
526*023e71deSHaik Aftandilian 		    "sending message: %d, attempts: %d", rv, resp->result,
527*023e71deSHaik Aftandilian 		    tries);
528*023e71deSHaik Aftandilian 	}
529*023e71deSHaik Aftandilian 
530*023e71deSHaik Aftandilian 	kmem_free(resp, sizeof (platsvc_suspend_resp_t) + reason_length);
531*023e71deSHaik Aftandilian }
532*023e71deSHaik Aftandilian 
533*023e71deSHaik Aftandilian /*
534*023e71deSHaik Aftandilian  * Handle data coming in for the suspend service. The suspend is
535*023e71deSHaik Aftandilian  * sequenced by the ps_suspend_thread, but perform some checks here
536*023e71deSHaik Aftandilian  * to make sure that the request is a valid request message and that
537*023e71deSHaik Aftandilian  * a suspend operation is not already in progress.
538*023e71deSHaik Aftandilian  */
539*023e71deSHaik Aftandilian /*ARGSUSED*/
540*023e71deSHaik Aftandilian static void
541*023e71deSHaik Aftandilian ps_suspend_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
542*023e71deSHaik Aftandilian {
543*023e71deSHaik Aftandilian 	platsvc_suspend_req_t	*msg = buf;
544*023e71deSHaik Aftandilian 
545*023e71deSHaik Aftandilian 	if (arg == NULL)
546*023e71deSHaik Aftandilian 		return;
547*023e71deSHaik Aftandilian 
548*023e71deSHaik Aftandilian 	if (ds_suspend_handle == DS_INVALID_HDL) {
549*023e71deSHaik Aftandilian 		DBG("ps_suspend_data_handler: DS handle no longer valid\n");
550*023e71deSHaik Aftandilian 		return;
551*023e71deSHaik Aftandilian 	}
552*023e71deSHaik Aftandilian 
553*023e71deSHaik Aftandilian 	/* Handle invalid requests */
554*023e71deSHaik Aftandilian 	if (msg == NULL || buflen != sizeof (platsvc_suspend_req_t) ||
555*023e71deSHaik Aftandilian 	    msg->type != DOMAIN_SUSPEND_SUSPEND) {
556*023e71deSHaik Aftandilian 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
557*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_INVALID_MSG, DOMAIN_SUSPEND_REC_SUCCESS,
558*023e71deSHaik Aftandilian 		    NULL, 0, 0);
559*023e71deSHaik Aftandilian 		return;
560*023e71deSHaik Aftandilian 	}
561*023e71deSHaik Aftandilian 
562*023e71deSHaik Aftandilian 	/*
563*023e71deSHaik Aftandilian 	 * If ps_suspend_thread_exit is set, ds_cap_fini has been
564*023e71deSHaik Aftandilian 	 * called and we shouldn't be receving data. Handle this unexpected
565*023e71deSHaik Aftandilian 	 * case by returning without sending a response.
566*023e71deSHaik Aftandilian 	 */
567*023e71deSHaik Aftandilian 	if (ps_suspend_thread_exit) {
568*023e71deSHaik Aftandilian 		DBG("ps_suspend_data_handler: ps_suspend_thread is exiting\n");
569*023e71deSHaik Aftandilian 		return;
570*023e71deSHaik Aftandilian 	}
571*023e71deSHaik Aftandilian 
572*023e71deSHaik Aftandilian 	mutex_enter(&ps_suspend_mutex);
573*023e71deSHaik Aftandilian 
574*023e71deSHaik Aftandilian 	/* If a suspend operation is in progress, abort now */
575*023e71deSHaik Aftandilian 	if (ps_suspend_data != NULL) {
576*023e71deSHaik Aftandilian 		mutex_exit(&ps_suspend_mutex);
577*023e71deSHaik Aftandilian 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
578*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_INPROGRESS, DOMAIN_SUSPEND_REC_SUCCESS,
579*023e71deSHaik Aftandilian 		    NULL, 0, 0);
580*023e71deSHaik Aftandilian 		return;
581*023e71deSHaik Aftandilian 	}
582*023e71deSHaik Aftandilian 
583*023e71deSHaik Aftandilian 	ps_suspend_data = kmem_alloc(sizeof (ps_suspend_data_t), KM_SLEEP);
584*023e71deSHaik Aftandilian 	ps_suspend_data->buf = kmem_alloc(buflen, KM_SLEEP);
585*023e71deSHaik Aftandilian 	ps_suspend_data->buflen = buflen;
586*023e71deSHaik Aftandilian 	bcopy(buf, ps_suspend_data->buf, buflen);
587*023e71deSHaik Aftandilian 
588*023e71deSHaik Aftandilian 	cv_signal(&ps_suspend_cv);
589*023e71deSHaik Aftandilian 	mutex_exit(&ps_suspend_mutex);
590*023e71deSHaik Aftandilian }
591*023e71deSHaik Aftandilian 
592*023e71deSHaik Aftandilian /*
593*023e71deSHaik Aftandilian  * Schedule the suspend operation by calling the pre-suspend, suspend,
594*023e71deSHaik Aftandilian  * and post-suspend functions. When sending back response messages, we
595*023e71deSHaik Aftandilian  * only use a timeout for the post-suspend response because after
596*023e71deSHaik Aftandilian  * a resume, domain services will be re-registered and we may not
597*023e71deSHaik Aftandilian  * be able to send the response immediately.
598*023e71deSHaik Aftandilian  */
599*023e71deSHaik Aftandilian static void
600*023e71deSHaik Aftandilian ps_suspend_sequence(ps_suspend_data_t *data)
601*023e71deSHaik Aftandilian {
602*023e71deSHaik Aftandilian 	platsvc_suspend_req_t	*msg;
603*023e71deSHaik Aftandilian 	uint32_t		rec_result;
604*023e71deSHaik Aftandilian 	char			*error_reason;
605*023e71deSHaik Aftandilian 	boolean_t		recovered = B_TRUE;
606*023e71deSHaik Aftandilian 	uint_t			rv;
607*023e71deSHaik Aftandilian 
608*023e71deSHaik Aftandilian 	ASSERT(data != NULL);
609*023e71deSHaik Aftandilian 
610*023e71deSHaik Aftandilian 	msg = data->buf;
611*023e71deSHaik Aftandilian 	error_reason = (char *)kmem_zalloc(SUSPEND_MAX_REASON_SIZE, KM_SLEEP);
612*023e71deSHaik Aftandilian 
613*023e71deSHaik Aftandilian 	/* Pre-suspend */
614*023e71deSHaik Aftandilian 	rv = suspend_pre(error_reason, SUSPEND_MAX_REASON_SIZE, &recovered);
615*023e71deSHaik Aftandilian 	if (rv != 0) {
616*023e71deSHaik Aftandilian 		rec_result = (recovered ? DOMAIN_SUSPEND_REC_SUCCESS :
617*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_REC_FAILURE);
618*023e71deSHaik Aftandilian 
619*023e71deSHaik Aftandilian 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
620*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_PRE_FAILURE, rec_result, error_reason, 0, 0);
621*023e71deSHaik Aftandilian 
622*023e71deSHaik Aftandilian 		kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
623*023e71deSHaik Aftandilian 		return;
624*023e71deSHaik Aftandilian 	}
625*023e71deSHaik Aftandilian 
626*023e71deSHaik Aftandilian 	ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
627*023e71deSHaik Aftandilian 	    DOMAIN_SUSPEND_PRE_SUCCESS, 0, NULL, 0, 0);
628*023e71deSHaik Aftandilian 
629*023e71deSHaik Aftandilian 	/* Suspend */
630*023e71deSHaik Aftandilian 	rv = suspend_start(error_reason, SUSPEND_MAX_REASON_SIZE);
631*023e71deSHaik Aftandilian 	if (rv != 0) {
632*023e71deSHaik Aftandilian 		rec_result = (suspend_post(NULL, 0) == 0 ?
633*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_REC_SUCCESS : DOMAIN_SUSPEND_REC_FAILURE);
634*023e71deSHaik Aftandilian 
635*023e71deSHaik Aftandilian 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
636*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_SUSPEND_FAILURE, rec_result, error_reason,
637*023e71deSHaik Aftandilian 		    0, 0);
638*023e71deSHaik Aftandilian 
639*023e71deSHaik Aftandilian 		kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
640*023e71deSHaik Aftandilian 		return;
641*023e71deSHaik Aftandilian 	}
642*023e71deSHaik Aftandilian 
643*023e71deSHaik Aftandilian 	/* Post-suspend */
644*023e71deSHaik Aftandilian 	rv = suspend_post(error_reason, SUSPEND_MAX_REASON_SIZE);
645*023e71deSHaik Aftandilian 	if (rv != 0) {
646*023e71deSHaik Aftandilian 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
647*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_POST_FAILURE, 0, error_reason,
648*023e71deSHaik Aftandilian 		    ps_suspend_rereg_delay, ps_suspend_retry_intval);
649*023e71deSHaik Aftandilian 	} else {
650*023e71deSHaik Aftandilian 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
651*023e71deSHaik Aftandilian 		    DOMAIN_SUSPEND_POST_SUCCESS, 0, error_reason,
652*023e71deSHaik Aftandilian 		    ps_suspend_rereg_delay, ps_suspend_retry_intval);
653*023e71deSHaik Aftandilian 	}
654*023e71deSHaik Aftandilian 
655*023e71deSHaik Aftandilian 	kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
656*023e71deSHaik Aftandilian }
657*023e71deSHaik Aftandilian 
658*023e71deSHaik Aftandilian /*
659*023e71deSHaik Aftandilian  * Wait for a suspend request or for ps_suspend_thread_exit to be set.
660*023e71deSHaik Aftandilian  */
661*023e71deSHaik Aftandilian static void
662*023e71deSHaik Aftandilian ps_suspend_thread_func(void)
663*023e71deSHaik Aftandilian {
664*023e71deSHaik Aftandilian 	mutex_enter(&ps_suspend_mutex);
665*023e71deSHaik Aftandilian 
666*023e71deSHaik Aftandilian 	while (ps_suspend_thread_exit == B_FALSE) {
667*023e71deSHaik Aftandilian 
668*023e71deSHaik Aftandilian 		if (ps_suspend_data == NULL) {
669*023e71deSHaik Aftandilian 			cv_wait(&ps_suspend_cv, &ps_suspend_mutex);
670*023e71deSHaik Aftandilian 			continue;
671*023e71deSHaik Aftandilian 		}
672*023e71deSHaik Aftandilian 
673*023e71deSHaik Aftandilian 		mutex_exit(&ps_suspend_mutex);
674*023e71deSHaik Aftandilian 		ps_suspend_sequence(ps_suspend_data);
675*023e71deSHaik Aftandilian 		mutex_enter(&ps_suspend_mutex);
676*023e71deSHaik Aftandilian 
677*023e71deSHaik Aftandilian 		kmem_free(ps_suspend_data->buf, ps_suspend_data->buflen);
678*023e71deSHaik Aftandilian 		kmem_free(ps_suspend_data, sizeof (ps_suspend_data_t));
679*023e71deSHaik Aftandilian 		ps_suspend_data = NULL;
680*023e71deSHaik Aftandilian 	}
681*023e71deSHaik Aftandilian 
682*023e71deSHaik Aftandilian 	mutex_exit(&ps_suspend_mutex);
683*023e71deSHaik Aftandilian 
684*023e71deSHaik Aftandilian 	thread_exit();
685*023e71deSHaik Aftandilian }
686*023e71deSHaik Aftandilian 
6871ae08745Sheppo static void
6881ae08745Sheppo ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
6891ae08745Sheppo {
6901ae08745Sheppo 	DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
6911ae08745Sheppo 	    arg, ver->major, ver->minor, hdl);
6921ae08745Sheppo 
6931ae08745Sheppo 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
6941ae08745Sheppo 		ds_md_handle = hdl;
6951ae08745Sheppo 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
6961ae08745Sheppo 		ds_shutdown_handle = hdl;
6971ae08745Sheppo 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
6981ae08745Sheppo 		ds_panic_handle = hdl;
699*023e71deSHaik Aftandilian 	if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
700*023e71deSHaik Aftandilian 		ds_suspend_handle = hdl;
7011ae08745Sheppo }
7021ae08745Sheppo 
7031ae08745Sheppo static void
7041ae08745Sheppo ps_unreg_handler(ds_cb_arg_t arg)
7051ae08745Sheppo {
7061ae08745Sheppo 	DBG("ps_unreg_handler: arg=0x%p\n", arg);
7071ae08745Sheppo 
7081ae08745Sheppo 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
7091ae08745Sheppo 		ds_md_handle = DS_INVALID_HDL;
7101ae08745Sheppo 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
7111ae08745Sheppo 		ds_shutdown_handle = DS_INVALID_HDL;
7121ae08745Sheppo 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
7131ae08745Sheppo 		ds_panic_handle = DS_INVALID_HDL;
714*023e71deSHaik Aftandilian 	if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
715*023e71deSHaik Aftandilian 		ds_suspend_handle = DS_INVALID_HDL;
7161ae08745Sheppo }
717