platsvc.c revision 02b4e56ca3a4e4a4fe9e52fca9c2972101f0e57f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* sun4v Platform Services Module
*/
#include <sys/machsystm.h>
/*
* Debugging routines
*/
#ifdef DEBUG
#else /* DEBUG */
#endif /* DEBUG */
/*
* Time resolution conversions.
*/
/*
* Domains Services interaction
*/
static ds_svc_hdl_t ds_md_handle;
static ds_svc_hdl_t ds_shutdown_handle;
static ds_svc_hdl_t ds_panic_handle;
static ds_svc_hdl_t ds_suspend_handle;
static ds_capability_t ps_md_cap = {
"md-update", /* svc_id */
ps_vers, /* vers */
PS_NVERS /* nvers */
};
static ds_capability_t ps_shutdown_cap = {
"domain-shutdown", /* svc_id */
ps_vers, /* vers */
PS_NVERS /* nvers */
};
static ds_capability_t ps_panic_cap = {
"domain-panic", /* svc_id */
ps_vers, /* vers */
PS_NVERS /* nvers */
};
static ds_capability_t ps_suspend_cap = {
"domain-suspend", /* svc_id */
ps_vers, /* vers */
PS_NVERS /* nvers */
};
static ds_clnt_ops_t ps_md_ops = {
ps_reg_handler, /* ds_reg_cb */
ps_unreg_handler, /* ds_unreg_cb */
ps_md_data_handler, /* ds_data_cb */
&ds_md_handle /* cb_arg */
};
static ds_clnt_ops_t ps_shutdown_ops = {
ps_reg_handler, /* ds_reg_cb */
ps_unreg_handler, /* ds_unreg_cb */
ps_shutdown_data_handler, /* ds_data_cb */
&ds_shutdown_handle /* cb_arg */
};
static ds_clnt_ops_t ps_panic_ops = {
ps_reg_handler, /* ds_reg_cb */
ps_unreg_handler, /* ds_unreg_cb */
ps_panic_data_handler, /* ds_data_cb */
&ds_panic_handle /* cb_arg */
};
static ds_clnt_ops_t ps_suspend_ops = {
ps_reg_handler, /* ds_reg_cb */
ps_unreg_handler, /* ds_unreg_cb */
ps_suspend_data_handler, /* ds_data_cb */
&ds_suspend_handle /* cb_arg */
};
static int ps_init(void);
static void ps_fini(void);
/*
* Power down timeout value of 5 minutes.
*/
#define PLATSVC_POWERDOWN_DELAY 1200
/*
* Set to true if OS suspend is supported. If OS suspend is not
* supported, the suspend service will not be started.
*/
/*
* Suspend service request handling
*/
typedef struct ps_suspend_data {
void *buf;
static kmutex_t ps_suspend_mutex;
static kcondvar_t ps_suspend_cv;
static void ps_suspend_thread_func(void);
/*
* The DELAY timeout is the time (in seconds) to wait for the
* operation. The INTVAL time is the time (in seconds) to wait
* between retry attempts when sending the post-suspend message
*/
#define PLATSVC_SUSPEND_REREG_DELAY 60
#define PLATSVC_SUSPEND_RETRY_INTVAL 1
static int ps_suspend_rereg_delay = PLATSVC_SUSPEND_REREG_DELAY;
static int ps_suspend_retry_intval = PLATSVC_SUSPEND_RETRY_INTVAL;
"sun4v Platform Services"
};
static struct modlinkage modlinkage = {
(void *)&modlmisc,
};
int
_init(void)
{
int rv;
return (rv);
ps_fini();
return (rv);
}
int
{
}
int platsvc_allow_unload;
int
_fini(void)
{
int status;
if (platsvc_allow_unload == 0)
return (EBUSY);
ps_fini();
return (status);
}
static int
ps_init(void)
{
int rv;
extern int mdeg_init(void);
extern void mdeg_fini(void);
/* register with domain services framework */
if (rv != 0) {
return (rv);
}
if (rv != 0) {
(void) ds_cap_fini(&ps_md_cap);
return (rv);
}
if (rv != 0) {
mdeg_fini();
(void) ds_cap_fini(&ps_md_cap);
return (rv);
}
if (rv != 0) {
(void) ds_cap_fini(&ps_md_cap);
mdeg_fini();
(void) ds_cap_fini(&ps_shutdown_cap);
return (rv);
}
if (ps_suspend_enabled) {
if (rv != 0) {
"%d", rv);
(void) ds_cap_fini(&ps_md_cap);
mdeg_fini();
(void) ds_cap_fini(&ps_shutdown_cap);
(void) ds_cap_fini(&ps_panic_cap);
return (rv);
}
}
return (0);
}
static void
ps_fini(void)
{
extern void mdeg_fini(void);
/*
* Stop incoming requests from Zeus
*/
(void) ds_cap_fini(&ps_md_cap);
(void) ds_cap_fini(&ps_shutdown_cap);
(void) ds_cap_fini(&ps_panic_cap);
if (ps_suspend_enabled) {
(void) ds_cap_fini(&ps_suspend_cap);
if (ps_suspend_thread != NULL) {
}
}
mdeg_fini();
}
static void
{
extern int mach_descrip_update(void);
extern void mdeg_notify_clients(void);
extern void recalc_xc_timeouts(void);
return;
if (ds_handle == DS_INVALID_HDL) {
DBG("ps_md_data_handler: DS handle no longer valid\n");
return;
}
sizeof (resp_msg))) != 0) {
}
return;
}
DBG("MD Reload...\n");
if (mach_descrip_update()) {
return;
}
/*
* notify registered clients that MD has
* been updated
*/
}
}
static void
{
return;
if (ds_handle == DS_INVALID_HDL) {
DBG("ps_shutdown_data_handler: DS handle no longer valid\n");
return;
}
sizeof (resp_msg))) != 0) {
rv);
}
return;
}
}
/*
* Honor the ldoms manager's shutdown delay requirement.
*/
;
}
static void
{
return;
if (ds_handle == DS_INVALID_HDL) {
DBG("ps_panic_data_handler: DS handle no longer valid\n");
return;
}
sizeof (resp_msg))) != 0) {
rv);
}
return;
}
}
}
/*
* Send a suspend response message. If a timeout is specified, wait
* intval seconds between attempts to send the message. The timeout
* and intval arguments are in seconds.
*/
static void
int intval)
{
int tries = 0;
int rv = -1;
reason_length = 0;
} else {
/* Get number of non-NULL bytes */
/* Account for NULL terminator */
}
resp = (platsvc_suspend_resp_t *)
KM_SLEEP);
if (reason_length > 0) {
/* Ensure NULL terminator is present */
}
if (timeout == 0) {
tries++;
sizeof (platsvc_suspend_resp_t) + reason_length);
} else {
do {
/*
* Copy the handle so we can ensure we never pass
* an invalid handle to ds_cap_send. We don't want
* to trigger warning messages just because the
* service was temporarily unregistered.
*/
sizeof (platsvc_suspend_resp_t) +
reason_length)) != 0) {
tries++;
}
}
if (rv != 0) {
tries);
}
}
/*
* Handle data coming in for the suspend service. The suspend is
* sequenced by the ps_suspend_thread, but perform some checks here
* to make sure that the request is a valid request message and that
* a suspend operation is not already in progress.
*/
/*ARGSUSED*/
static void
{
return;
if (ds_suspend_handle == DS_INVALID_HDL) {
DBG("ps_suspend_data_handler: DS handle no longer valid\n");
return;
}
/* Handle invalid requests */
NULL, 0, 0);
return;
}
/*
* If ps_suspend_thread_exit is set, ds_cap_fini has been
* called and we shouldn't be receving data. Handle this unexpected
* case by returning without sending a response.
*/
if (ps_suspend_thread_exit) {
DBG("ps_suspend_data_handler: ps_suspend_thread is exiting\n");
return;
}
/* If a suspend operation is in progress, abort now */
if (ps_suspend_data != NULL) {
NULL, 0, 0);
return;
}
}
/*
* Schedule the suspend operation by calling the pre-suspend, suspend,
* and post-suspend functions. When sending back response messages, we
* only use a timeout for the post-suspend response because after
* a resume, domain services will be re-registered and we may not
* be able to send the response immediately.
*/
static void
{
char *error_reason;
int dr_block;
/*
* Abort the suspend if a DR operation is in progress. Otherwise,
* continue whilst blocking any new DR operations.
*/
if ((dr_block = drctl_tryblock()) == 0) {
/* Pre-suspend */
&recovered);
} else {
/* A DR operation is in progress */
}
if (dr_block == 0)
return;
}
DOMAIN_SUSPEND_PRE_SUCCESS, 0, NULL, 0, 0);
/* Suspend */
if (rv != 0) {
0, 0);
return;
}
/* Post-suspend */
if (rv != 0) {
} else {
}
}
/*
* Wait for a suspend request or for ps_suspend_thread_exit to be set.
*/
static void
ps_suspend_thread_func(void)
{
while (ps_suspend_thread_exit == B_FALSE) {
if (ps_suspend_data == NULL) {
continue;
}
}
thread_exit();
}
static void
{
DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
ds_md_handle = hdl;
}
static void
{
}