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 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); 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 /* 1361ae08745Sheppo * 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 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); 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(); 2621ae08745Sheppo 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); 2781ae08745Sheppo return (rv); 2791ae08745Sheppo } 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); 286023e71deSHaik Aftandilian } 287023e71deSHaik 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 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 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 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 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 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 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) { 619023e71deSHaik 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 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 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 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