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*02b4e56cSHaik Aftandilian * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241ae08745Sheppo */
251ae08745Sheppo
261ae08745Sheppo /*
271ae08745Sheppo * sun4v Platform Services Module
281ae08745Sheppo */
291ae08745Sheppo
301ae08745Sheppo #include <sys/modctl.h>
311ae08745Sheppo #include <sys/cmn_err.h>
321ae08745Sheppo #include <sys/machsystm.h>
331ae08745Sheppo #include <sys/note.h>
341ae08745Sheppo #include <sys/uadmin.h>
351ae08745Sheppo #include <sys/ds.h>
361ae08745Sheppo #include <sys/platsvc.h>
37023e71deSHaik Aftandilian #include <sys/ddi.h>
38023e71deSHaik Aftandilian #include <sys/suspend.h>
39023e71deSHaik Aftandilian #include <sys/proc.h>
40023e71deSHaik Aftandilian #include <sys/disp.h>
41*02b4e56cSHaik Aftandilian #include <sys/drctl.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)
59023e71deSHaik 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;
67023e71deSHaik 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
90023e71deSHaik Aftandilian static ds_capability_t ps_suspend_cap = {
91023e71deSHaik Aftandilian "domain-suspend", /* svc_id */
92023e71deSHaik Aftandilian ps_vers, /* vers */
93023e71deSHaik Aftandilian PS_NVERS /* nvers */
94023e71deSHaik Aftandilian };
95023e71deSHaik 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
99023e71deSHaik Aftandilian 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);
101023e71deSHaik Aftandilian static void ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
102023e71deSHaik 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
125023e71deSHaik Aftandilian static ds_clnt_ops_t ps_suspend_ops = {
126023e71deSHaik Aftandilian ps_reg_handler, /* ds_reg_cb */
127023e71deSHaik Aftandilian ps_unreg_handler, /* ds_unreg_cb */
128023e71deSHaik Aftandilian ps_suspend_data_handler, /* ds_data_cb */
129023e71deSHaik Aftandilian &ds_suspend_handle /* cb_arg */
130023e71deSHaik Aftandilian };
131023e71deSHaik Aftandilian
1321ae08745Sheppo static int ps_init(void);
1331ae08745Sheppo static void ps_fini(void);
1341ae08745Sheppo
1351ae08745Sheppo /*
1364e476149Srsmaeda * Power down timeout value of 5 minutes.
1371ae08745Sheppo */
1381ae08745Sheppo #define PLATSVC_POWERDOWN_DELAY 1200
1391ae08745Sheppo
140023e71deSHaik Aftandilian /*
141023e71deSHaik Aftandilian * Set to true if OS suspend is supported. If OS suspend is not
142023e71deSHaik Aftandilian * supported, the suspend service will not be started.
143023e71deSHaik Aftandilian */
144023e71deSHaik Aftandilian static boolean_t ps_suspend_enabled = B_FALSE;
145023e71deSHaik Aftandilian
146023e71deSHaik Aftandilian /*
147023e71deSHaik Aftandilian * Suspend service request handling
148023e71deSHaik Aftandilian */
149023e71deSHaik Aftandilian typedef struct ps_suspend_data {
150023e71deSHaik Aftandilian void *buf;
151023e71deSHaik Aftandilian size_t buflen;
152023e71deSHaik Aftandilian } ps_suspend_data_t;
153023e71deSHaik Aftandilian
154023e71deSHaik Aftandilian static kmutex_t ps_suspend_mutex;
155023e71deSHaik Aftandilian static kcondvar_t ps_suspend_cv;
156023e71deSHaik Aftandilian
157023e71deSHaik Aftandilian static ps_suspend_data_t *ps_suspend_data = NULL;
158023e71deSHaik Aftandilian static boolean_t ps_suspend_thread_exit = B_FALSE;
159023e71deSHaik Aftandilian static kthread_t *ps_suspend_thread = NULL;
160023e71deSHaik Aftandilian
161023e71deSHaik Aftandilian static void ps_suspend_sequence(ps_suspend_data_t *data);
162023e71deSHaik Aftandilian static void ps_suspend_thread_func(void);
163023e71deSHaik Aftandilian
164023e71deSHaik Aftandilian /*
165023e71deSHaik Aftandilian * The DELAY timeout is the time (in seconds) to wait for the
166023e71deSHaik Aftandilian * suspend service to be re-registered after a suspend/resume
167023e71deSHaik Aftandilian * operation. The INTVAL time is the time (in seconds) to wait
168023e71deSHaik Aftandilian * between retry attempts when sending the post-suspend message
169023e71deSHaik Aftandilian * after a suspend/resume operation.
170023e71deSHaik Aftandilian */
171023e71deSHaik Aftandilian #define PLATSVC_SUSPEND_REREG_DELAY 60
172023e71deSHaik Aftandilian #define PLATSVC_SUSPEND_RETRY_INTVAL 1
173023e71deSHaik Aftandilian static int ps_suspend_rereg_delay = PLATSVC_SUSPEND_REREG_DELAY;
174023e71deSHaik Aftandilian static int ps_suspend_retry_intval = PLATSVC_SUSPEND_RETRY_INTVAL;
175023e71deSHaik Aftandilian
176023e71deSHaik 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
_init(void)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
_info(struct modinfo * modinfop)2031ae08745Sheppo _info(struct modinfo *modinfop)
2041ae08745Sheppo {
2051ae08745Sheppo return (mod_info(&modlinkage, modinfop));
2061ae08745Sheppo }
2071ae08745Sheppo
2081ae08745Sheppo int platsvc_allow_unload;
2091ae08745Sheppo
2101ae08745Sheppo int
_fini(void)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
ps_init(void)2251ae08745Sheppo ps_init(void)
2261ae08745Sheppo {
2271ae08745Sheppo int rv;
2281ae08745Sheppo extern int mdeg_init(void);
229023e71deSHaik 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
238023e71deSHaik Aftandilian rv = mdeg_init();
239023e71deSHaik Aftandilian if (rv != 0) {
240023e71deSHaik Aftandilian (void) ds_cap_fini(&ps_md_cap);
241023e71deSHaik Aftandilian return (rv);
242023e71deSHaik Aftandilian }
243023e71deSHaik 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);
247023e71deSHaik 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);
256023e71deSHaik Aftandilian mdeg_fini();
2571ae08745Sheppo (void) ds_cap_fini(&ps_shutdown_cap);
2581ae08745Sheppo return (rv);
2591ae08745Sheppo }
2601ae08745Sheppo
261023e71deSHaik Aftandilian ps_suspend_enabled = suspend_supported();
262023e71deSHaik Aftandilian
263023e71deSHaik Aftandilian if (ps_suspend_enabled) {
264023e71deSHaik Aftandilian mutex_init(&ps_suspend_mutex, NULL, MUTEX_DEFAULT, NULL);
265023e71deSHaik Aftandilian cv_init(&ps_suspend_cv, NULL, CV_DEFAULT, NULL);
266023e71deSHaik Aftandilian ps_suspend_thread_exit = B_FALSE;
267023e71deSHaik Aftandilian
268023e71deSHaik Aftandilian rv = ds_cap_init(&ps_suspend_cap, &ps_suspend_ops);
269023e71deSHaik Aftandilian if (rv != 0) {
270023e71deSHaik Aftandilian cmn_err(CE_WARN, "ds_cap_init domain-suspend failed: "
271023e71deSHaik Aftandilian "%d", rv);
272023e71deSHaik Aftandilian (void) ds_cap_fini(&ps_md_cap);
273023e71deSHaik Aftandilian mdeg_fini();
274023e71deSHaik Aftandilian (void) ds_cap_fini(&ps_shutdown_cap);
275023e71deSHaik Aftandilian (void) ds_cap_fini(&ps_panic_cap);
276023e71deSHaik Aftandilian mutex_destroy(&ps_suspend_mutex);
277023e71deSHaik Aftandilian cv_destroy(&ps_suspend_cv);
278023e71deSHaik Aftandilian return (rv);
279023e71deSHaik Aftandilian }
2801ae08745Sheppo
281023e71deSHaik Aftandilian ps_suspend_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
282023e71deSHaik Aftandilian ps_suspend_thread_func, NULL, 0, &p0, TS_RUN, minclsyspri);
283023e71deSHaik Aftandilian }
284023e71deSHaik Aftandilian
285023e71deSHaik Aftandilian return (0);
2861ae08745Sheppo }
2871ae08745Sheppo
2881ae08745Sheppo static void
ps_fini(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
300023e71deSHaik Aftandilian if (ps_suspend_enabled) {
301023e71deSHaik Aftandilian (void) ds_cap_fini(&ps_suspend_cap);
302023e71deSHaik Aftandilian if (ps_suspend_thread != NULL) {
303023e71deSHaik Aftandilian mutex_enter(&ps_suspend_mutex);
304023e71deSHaik Aftandilian ps_suspend_thread_exit = B_TRUE;
305023e71deSHaik Aftandilian cv_signal(&ps_suspend_cv);
306023e71deSHaik Aftandilian mutex_exit(&ps_suspend_mutex);
307023e71deSHaik Aftandilian
308023e71deSHaik Aftandilian thread_join(ps_suspend_thread->t_did);
309023e71deSHaik Aftandilian ps_suspend_thread = NULL;
310023e71deSHaik Aftandilian
311023e71deSHaik Aftandilian mutex_destroy(&ps_suspend_mutex);
312023e71deSHaik Aftandilian cv_destroy(&ps_suspend_cv);
313023e71deSHaik Aftandilian }
314023e71deSHaik Aftandilian }
315023e71deSHaik Aftandilian
3161ae08745Sheppo mdeg_fini();
3171ae08745Sheppo }
3181ae08745Sheppo
3191ae08745Sheppo static void
ps_md_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)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);
324f273041fSjm 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
355f273041fSjm recalc_xc_timeouts();
356f273041fSjm
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
ps_shutdown_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)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
ps_panic_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)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
460023e71deSHaik Aftandilian /*
461023e71deSHaik Aftandilian * Send a suspend response message. If a timeout is specified, wait
462023e71deSHaik Aftandilian * intval seconds between attempts to send the message. The timeout
463023e71deSHaik Aftandilian * and intval arguments are in seconds.
464023e71deSHaik Aftandilian */
465023e71deSHaik Aftandilian static void
ps_suspend_send_response(ds_svc_hdl_t * ds_handle,uint64_t req_num,uint32_t result,uint32_t rec_result,char * reason,int timeout,int intval)466023e71deSHaik Aftandilian ps_suspend_send_response(ds_svc_hdl_t *ds_handle, uint64_t req_num,
467023e71deSHaik Aftandilian uint32_t result, uint32_t rec_result, char *reason, int timeout,
468023e71deSHaik Aftandilian int intval)
469023e71deSHaik Aftandilian {
470023e71deSHaik Aftandilian platsvc_suspend_resp_t *resp;
471023e71deSHaik Aftandilian size_t reason_length;
472023e71deSHaik Aftandilian int tries = 0;
473023e71deSHaik Aftandilian int rv = -1;
474023e71deSHaik Aftandilian time_t deadline;
475023e71deSHaik Aftandilian
476023e71deSHaik Aftandilian if (reason == NULL) {
477023e71deSHaik Aftandilian reason_length = 0;
478023e71deSHaik Aftandilian } else {
479023e71deSHaik Aftandilian /* Get number of non-NULL bytes */
480023e71deSHaik Aftandilian reason_length = strnlen(reason, SUSPEND_MAX_REASON_SIZE - 1);
481023e71deSHaik Aftandilian ASSERT(reason[reason_length] == '\0');
482023e71deSHaik Aftandilian /* Account for NULL terminator */
483023e71deSHaik Aftandilian reason_length++;
484023e71deSHaik Aftandilian }
485023e71deSHaik Aftandilian
486023e71deSHaik Aftandilian resp = (platsvc_suspend_resp_t *)
487023e71deSHaik Aftandilian kmem_zalloc(sizeof (platsvc_suspend_resp_t) + reason_length,
488023e71deSHaik Aftandilian KM_SLEEP);
489023e71deSHaik Aftandilian
490023e71deSHaik Aftandilian resp->req_num = req_num;
491023e71deSHaik Aftandilian resp->result = result;
492023e71deSHaik Aftandilian resp->rec_result = rec_result;
493023e71deSHaik Aftandilian if (reason_length > 0) {
494023e71deSHaik Aftandilian bcopy(reason, &resp->reason, reason_length - 1);
495023e71deSHaik Aftandilian /* Ensure NULL terminator is present */
496023e71deSHaik Aftandilian resp->reason[reason_length] = '\0';
497023e71deSHaik Aftandilian }
498023e71deSHaik Aftandilian
499023e71deSHaik Aftandilian if (timeout == 0) {
500023e71deSHaik Aftandilian tries++;
501023e71deSHaik Aftandilian rv = ds_cap_send(*ds_handle, resp,
502023e71deSHaik Aftandilian sizeof (platsvc_suspend_resp_t) + reason_length);
503023e71deSHaik Aftandilian } else {
504023e71deSHaik Aftandilian deadline = gethrestime_sec() + timeout;
505023e71deSHaik Aftandilian do {
506023e71deSHaik Aftandilian ds_svc_hdl_t hdl;
507023e71deSHaik Aftandilian /*
508023e71deSHaik Aftandilian * Copy the handle so we can ensure we never pass
509023e71deSHaik Aftandilian * an invalid handle to ds_cap_send. We don't want
510023e71deSHaik Aftandilian * to trigger warning messages just because the
511023e71deSHaik Aftandilian * service was temporarily unregistered.
512023e71deSHaik Aftandilian */
513023e71deSHaik Aftandilian if ((hdl = *ds_handle) == DS_INVALID_HDL) {
514023e71deSHaik Aftandilian delay(SEC2HZ(intval));
515023e71deSHaik Aftandilian } else if ((rv = ds_cap_send(hdl, resp,
516023e71deSHaik Aftandilian sizeof (platsvc_suspend_resp_t) +
517023e71deSHaik Aftandilian reason_length)) != 0) {
518023e71deSHaik Aftandilian tries++;
519023e71deSHaik Aftandilian delay(SEC2HZ(intval));
520023e71deSHaik Aftandilian }
521023e71deSHaik Aftandilian } while ((rv != 0) && (gethrestime_sec() < deadline));
522023e71deSHaik Aftandilian }
523023e71deSHaik Aftandilian
524023e71deSHaik Aftandilian if (rv != 0) {
525023e71deSHaik Aftandilian cmn_err(CE_NOTE, "suspend ds_cap_send resp failed (%d) "
526023e71deSHaik Aftandilian "sending message: %d, attempts: %d", rv, resp->result,
527023e71deSHaik Aftandilian tries);
528023e71deSHaik Aftandilian }
529023e71deSHaik Aftandilian
530023e71deSHaik Aftandilian kmem_free(resp, sizeof (platsvc_suspend_resp_t) + reason_length);
531023e71deSHaik Aftandilian }
532023e71deSHaik Aftandilian
533023e71deSHaik Aftandilian /*
534023e71deSHaik Aftandilian * Handle data coming in for the suspend service. The suspend is
535023e71deSHaik Aftandilian * sequenced by the ps_suspend_thread, but perform some checks here
536023e71deSHaik Aftandilian * to make sure that the request is a valid request message and that
537023e71deSHaik Aftandilian * a suspend operation is not already in progress.
538023e71deSHaik Aftandilian */
539023e71deSHaik Aftandilian /*ARGSUSED*/
540023e71deSHaik Aftandilian static void
ps_suspend_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)541023e71deSHaik Aftandilian ps_suspend_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
542023e71deSHaik Aftandilian {
543023e71deSHaik Aftandilian platsvc_suspend_req_t *msg = buf;
544023e71deSHaik Aftandilian
545023e71deSHaik Aftandilian if (arg == NULL)
546023e71deSHaik Aftandilian return;
547023e71deSHaik Aftandilian
548023e71deSHaik Aftandilian if (ds_suspend_handle == DS_INVALID_HDL) {
549023e71deSHaik Aftandilian DBG("ps_suspend_data_handler: DS handle no longer valid\n");
550023e71deSHaik Aftandilian return;
551023e71deSHaik Aftandilian }
552023e71deSHaik Aftandilian
553023e71deSHaik Aftandilian /* Handle invalid requests */
554023e71deSHaik Aftandilian if (msg == NULL || buflen != sizeof (platsvc_suspend_req_t) ||
555023e71deSHaik Aftandilian msg->type != DOMAIN_SUSPEND_SUSPEND) {
556023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
557023e71deSHaik Aftandilian DOMAIN_SUSPEND_INVALID_MSG, DOMAIN_SUSPEND_REC_SUCCESS,
558023e71deSHaik Aftandilian NULL, 0, 0);
559023e71deSHaik Aftandilian return;
560023e71deSHaik Aftandilian }
561023e71deSHaik Aftandilian
562023e71deSHaik Aftandilian /*
563023e71deSHaik Aftandilian * If ps_suspend_thread_exit is set, ds_cap_fini has been
564023e71deSHaik Aftandilian * called and we shouldn't be receving data. Handle this unexpected
565023e71deSHaik Aftandilian * case by returning without sending a response.
566023e71deSHaik Aftandilian */
567023e71deSHaik Aftandilian if (ps_suspend_thread_exit) {
568023e71deSHaik Aftandilian DBG("ps_suspend_data_handler: ps_suspend_thread is exiting\n");
569023e71deSHaik Aftandilian return;
570023e71deSHaik Aftandilian }
571023e71deSHaik Aftandilian
572023e71deSHaik Aftandilian mutex_enter(&ps_suspend_mutex);
573023e71deSHaik Aftandilian
574023e71deSHaik Aftandilian /* If a suspend operation is in progress, abort now */
575023e71deSHaik Aftandilian if (ps_suspend_data != NULL) {
576023e71deSHaik Aftandilian mutex_exit(&ps_suspend_mutex);
577023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
578023e71deSHaik Aftandilian DOMAIN_SUSPEND_INPROGRESS, DOMAIN_SUSPEND_REC_SUCCESS,
579023e71deSHaik Aftandilian NULL, 0, 0);
580023e71deSHaik Aftandilian return;
581023e71deSHaik Aftandilian }
582023e71deSHaik Aftandilian
583023e71deSHaik Aftandilian ps_suspend_data = kmem_alloc(sizeof (ps_suspend_data_t), KM_SLEEP);
584023e71deSHaik Aftandilian ps_suspend_data->buf = kmem_alloc(buflen, KM_SLEEP);
585023e71deSHaik Aftandilian ps_suspend_data->buflen = buflen;
586023e71deSHaik Aftandilian bcopy(buf, ps_suspend_data->buf, buflen);
587023e71deSHaik Aftandilian
588023e71deSHaik Aftandilian cv_signal(&ps_suspend_cv);
589023e71deSHaik Aftandilian mutex_exit(&ps_suspend_mutex);
590023e71deSHaik Aftandilian }
591023e71deSHaik Aftandilian
592023e71deSHaik Aftandilian /*
593023e71deSHaik Aftandilian * Schedule the suspend operation by calling the pre-suspend, suspend,
594023e71deSHaik Aftandilian * and post-suspend functions. When sending back response messages, we
595023e71deSHaik Aftandilian * only use a timeout for the post-suspend response because after
596023e71deSHaik Aftandilian * a resume, domain services will be re-registered and we may not
597023e71deSHaik Aftandilian * be able to send the response immediately.
598023e71deSHaik Aftandilian */
599023e71deSHaik Aftandilian static void
ps_suspend_sequence(ps_suspend_data_t * data)600023e71deSHaik Aftandilian ps_suspend_sequence(ps_suspend_data_t *data)
601023e71deSHaik Aftandilian {
602023e71deSHaik Aftandilian platsvc_suspend_req_t *msg;
603023e71deSHaik Aftandilian uint32_t rec_result;
604023e71deSHaik Aftandilian char *error_reason;
605023e71deSHaik Aftandilian boolean_t recovered = B_TRUE;
606*02b4e56cSHaik Aftandilian uint_t rv = 0;
607*02b4e56cSHaik Aftandilian int dr_block;
608023e71deSHaik Aftandilian
609023e71deSHaik Aftandilian ASSERT(data != NULL);
610023e71deSHaik Aftandilian
611023e71deSHaik Aftandilian msg = data->buf;
612023e71deSHaik Aftandilian error_reason = (char *)kmem_zalloc(SUSPEND_MAX_REASON_SIZE, KM_SLEEP);
613023e71deSHaik Aftandilian
614*02b4e56cSHaik Aftandilian /*
615*02b4e56cSHaik Aftandilian * Abort the suspend if a DR operation is in progress. Otherwise,
616*02b4e56cSHaik Aftandilian * continue whilst blocking any new DR operations.
617*02b4e56cSHaik Aftandilian */
618*02b4e56cSHaik Aftandilian if ((dr_block = drctl_tryblock()) == 0) {
619*02b4e56cSHaik Aftandilian /* Pre-suspend */
620*02b4e56cSHaik Aftandilian rv = suspend_pre(error_reason, SUSPEND_MAX_REASON_SIZE,
621*02b4e56cSHaik Aftandilian &recovered);
622*02b4e56cSHaik Aftandilian } else {
623*02b4e56cSHaik Aftandilian /* A DR operation is in progress */
624*02b4e56cSHaik Aftandilian (void) strncpy(error_reason, DOMAIN_SUSPEND_DR_ERROR_STR,
625*02b4e56cSHaik Aftandilian SUSPEND_MAX_REASON_SIZE);
626*02b4e56cSHaik Aftandilian }
627*02b4e56cSHaik Aftandilian
628*02b4e56cSHaik Aftandilian if (dr_block != 0 || rv != 0) {
629023e71deSHaik Aftandilian rec_result = (recovered ? DOMAIN_SUSPEND_REC_SUCCESS :
630023e71deSHaik Aftandilian DOMAIN_SUSPEND_REC_FAILURE);
631023e71deSHaik Aftandilian
632023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
633023e71deSHaik Aftandilian DOMAIN_SUSPEND_PRE_FAILURE, rec_result, error_reason, 0, 0);
634023e71deSHaik Aftandilian
635*02b4e56cSHaik Aftandilian if (dr_block == 0)
636*02b4e56cSHaik Aftandilian drctl_unblock();
637023e71deSHaik Aftandilian kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
638023e71deSHaik Aftandilian return;
639023e71deSHaik Aftandilian }
640023e71deSHaik Aftandilian
641023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
642023e71deSHaik Aftandilian DOMAIN_SUSPEND_PRE_SUCCESS, 0, NULL, 0, 0);
643023e71deSHaik Aftandilian
644023e71deSHaik Aftandilian /* Suspend */
645023e71deSHaik Aftandilian rv = suspend_start(error_reason, SUSPEND_MAX_REASON_SIZE);
646023e71deSHaik Aftandilian if (rv != 0) {
647023e71deSHaik Aftandilian rec_result = (suspend_post(NULL, 0) == 0 ?
648023e71deSHaik Aftandilian DOMAIN_SUSPEND_REC_SUCCESS : DOMAIN_SUSPEND_REC_FAILURE);
649023e71deSHaik Aftandilian
650023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
651023e71deSHaik Aftandilian DOMAIN_SUSPEND_SUSPEND_FAILURE, rec_result, error_reason,
652023e71deSHaik Aftandilian 0, 0);
653023e71deSHaik Aftandilian
654*02b4e56cSHaik Aftandilian drctl_unblock();
655023e71deSHaik Aftandilian kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
656023e71deSHaik Aftandilian return;
657023e71deSHaik Aftandilian }
658023e71deSHaik Aftandilian
659023e71deSHaik Aftandilian /* Post-suspend */
660023e71deSHaik Aftandilian rv = suspend_post(error_reason, SUSPEND_MAX_REASON_SIZE);
661023e71deSHaik Aftandilian if (rv != 0) {
662023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
663023e71deSHaik Aftandilian DOMAIN_SUSPEND_POST_FAILURE, 0, error_reason,
664023e71deSHaik Aftandilian ps_suspend_rereg_delay, ps_suspend_retry_intval);
665023e71deSHaik Aftandilian } else {
666023e71deSHaik Aftandilian ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
667023e71deSHaik Aftandilian DOMAIN_SUSPEND_POST_SUCCESS, 0, error_reason,
668023e71deSHaik Aftandilian ps_suspend_rereg_delay, ps_suspend_retry_intval);
669023e71deSHaik Aftandilian }
670023e71deSHaik Aftandilian
671*02b4e56cSHaik Aftandilian drctl_unblock();
672023e71deSHaik Aftandilian kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
673023e71deSHaik Aftandilian }
674023e71deSHaik Aftandilian
675023e71deSHaik Aftandilian /*
676023e71deSHaik Aftandilian * Wait for a suspend request or for ps_suspend_thread_exit to be set.
677023e71deSHaik Aftandilian */
678023e71deSHaik Aftandilian static void
ps_suspend_thread_func(void)679023e71deSHaik Aftandilian ps_suspend_thread_func(void)
680023e71deSHaik Aftandilian {
681023e71deSHaik Aftandilian mutex_enter(&ps_suspend_mutex);
682023e71deSHaik Aftandilian
683023e71deSHaik Aftandilian while (ps_suspend_thread_exit == B_FALSE) {
684023e71deSHaik Aftandilian
685023e71deSHaik Aftandilian if (ps_suspend_data == NULL) {
686023e71deSHaik Aftandilian cv_wait(&ps_suspend_cv, &ps_suspend_mutex);
687023e71deSHaik Aftandilian continue;
688023e71deSHaik Aftandilian }
689023e71deSHaik Aftandilian
690023e71deSHaik Aftandilian mutex_exit(&ps_suspend_mutex);
691023e71deSHaik Aftandilian ps_suspend_sequence(ps_suspend_data);
692023e71deSHaik Aftandilian mutex_enter(&ps_suspend_mutex);
693023e71deSHaik Aftandilian
694023e71deSHaik Aftandilian kmem_free(ps_suspend_data->buf, ps_suspend_data->buflen);
695023e71deSHaik Aftandilian kmem_free(ps_suspend_data, sizeof (ps_suspend_data_t));
696023e71deSHaik Aftandilian ps_suspend_data = NULL;
697023e71deSHaik Aftandilian }
698023e71deSHaik Aftandilian
699023e71deSHaik Aftandilian mutex_exit(&ps_suspend_mutex);
700023e71deSHaik Aftandilian
701023e71deSHaik Aftandilian thread_exit();
702023e71deSHaik Aftandilian }
703023e71deSHaik Aftandilian
7041ae08745Sheppo static void
ps_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)7051ae08745Sheppo ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
7061ae08745Sheppo {
7071ae08745Sheppo DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
7081ae08745Sheppo arg, ver->major, ver->minor, hdl);
7091ae08745Sheppo
7101ae08745Sheppo if ((ds_svc_hdl_t *)arg == &ds_md_handle)
7111ae08745Sheppo ds_md_handle = hdl;
7121ae08745Sheppo if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
7131ae08745Sheppo ds_shutdown_handle = hdl;
7141ae08745Sheppo if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
7151ae08745Sheppo ds_panic_handle = hdl;
716023e71deSHaik Aftandilian if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
717023e71deSHaik Aftandilian ds_suspend_handle = hdl;
7181ae08745Sheppo }
7191ae08745Sheppo
7201ae08745Sheppo static void
ps_unreg_handler(ds_cb_arg_t arg)7211ae08745Sheppo ps_unreg_handler(ds_cb_arg_t arg)
7221ae08745Sheppo {
7231ae08745Sheppo DBG("ps_unreg_handler: arg=0x%p\n", arg);
7241ae08745Sheppo
7251ae08745Sheppo if ((ds_svc_hdl_t *)arg == &ds_md_handle)
7261ae08745Sheppo ds_md_handle = DS_INVALID_HDL;
7271ae08745Sheppo if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
7281ae08745Sheppo ds_shutdown_handle = DS_INVALID_HDL;
7291ae08745Sheppo if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
7301ae08745Sheppo ds_panic_handle = DS_INVALID_HDL;
731023e71deSHaik Aftandilian if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
732023e71deSHaik Aftandilian ds_suspend_handle = DS_INVALID_HDL;
7331ae08745Sheppo }
734