platsvc.c revision 4e4761498aa6990539820cfc2ee7b1c7c53b6bc3
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* sun4v Platform Services Module
*/
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/machsystm.h>
#include <sys/note.h>
#include <sys/uadmin.h>
#include <sys/ds.h>
#include <sys/platsvc.h>
/*
* Debugging routines
*/
#ifdef DEBUG
uint_t ps_debug = 0x0;
#define DBG if (ps_debug) printf
#else /* DEBUG */
#define DBG _NOTE(CONSTCOND) if (0) printf
#endif /* DEBUG */
/*
* Time resolution conversions.
*/
#define MS2NANO(x) ((x) * MICROSEC)
#define MS2SEC(x) ((x) / MILLISEC)
#define MS2MIN(x) (MS2SEC(x) / 60)
/*
* 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_ver_t ps_vers[] = {{ 1, 0 }};
#define PS_NVERS (sizeof (ps_vers) / sizeof (ps_vers[0]))
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 void ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
static void ps_unreg_handler(ds_cb_arg_t arg);
static void ps_md_data_handler(ds_cb_arg_t arg, void * buf, size_t buflen);
static void ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
static void ps_panic_data_handler(ds_cb_arg_t arg, void * buf, size_t buflen);
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 int ps_init(void);
static void ps_fini(void);
/*
* Power down timeout value of 5 minutes.
*/
#define PLATSVC_POWERDOWN_DELAY 1200
static struct modlmisc modlmisc = {
&mod_miscops,
"sun4v Platform Services %I%"
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modlmisc,
NULL
};
int
_init(void)
{
int rv;
if ((rv = ps_init()) != 0)
return (rv);
if ((rv = mod_install(&modlinkage)) != 0)
ps_fini();
return (rv);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int platsvc_allow_unload;
int
_fini(void)
{
int status;
if (platsvc_allow_unload == 0)
return (EBUSY);
if ((status = mod_remove(&modlinkage)) == 0)
ps_fini();
return (status);
}
static int
ps_init(void)
{
int rv;
extern int mdeg_init(void);
/* register with domain services framework */
rv = ds_cap_init(&ps_md_cap, &ps_md_ops);
if (rv != 0) {
cmn_err(CE_WARN, "ds_cap_init md-update failed: %d", rv);
return (rv);
}
rv = ds_cap_init(&ps_shutdown_cap, &ps_shutdown_ops);
if (rv != 0) {
cmn_err(CE_WARN, "ds_cap_init domain-shutdown failed: %d", rv);
(void) ds_cap_fini(&ps_md_cap);
return (rv);
}
rv = ds_cap_init(&ps_panic_cap, &ps_panic_ops);
if (rv != 0) {
cmn_err(CE_WARN, "ds_cap_init domain-panic failed: %d", rv);
(void) ds_cap_fini(&ps_md_cap);
(void) ds_cap_fini(&ps_shutdown_cap);
return (rv);
}
rv = mdeg_init();
return (rv);
}
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);
mdeg_fini();
}
static void
ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
{
extern int mach_descrip_update(void);
extern void mdeg_notify_clients(void);
extern void recalc_xc_timeouts(void);
ds_svc_hdl_t ds_handle = ds_md_handle;
platsvc_md_update_req_t *msg = buf;
platsvc_md_update_resp_t resp_msg;
uint_t rv;
if (arg == NULL)
return;
if (ds_handle == DS_INVALID_HDL) {
DBG("ps_md_data_handler: DS handle no longer valid\n");
return;
}
if (msg == NULL || buflen != sizeof (platsvc_md_update_req_t)) {
resp_msg.req_num = 0;
resp_msg.result = MD_UPDATE_INVALID_MSG;
if ((rv = ds_cap_send(ds_handle, &resp_msg,
sizeof (resp_msg))) != 0) {
cmn_err(CE_NOTE, "md ds_cap_send failed (%d)", rv);
}
return;
}
DBG("MD Reload...\n");
if (mach_descrip_update()) {
cmn_err(CE_WARN, "MD reload failed\n");
return;
}
recalc_xc_timeouts();
/*
* notify registered clients that MD has
* been updated
*/
mdeg_notify_clients();
resp_msg.req_num = msg->req_num;
resp_msg.result = MD_UPDATE_SUCCESS;
if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
cmn_err(CE_NOTE, "md ds_cap_send resp failed (%d)", rv);
}
}
static void
ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
{
ds_svc_hdl_t ds_handle = ds_shutdown_handle;
platsvc_shutdown_req_t *msg = buf;
platsvc_shutdown_resp_t resp_msg;
uint_t rv;
hrtime_t start;
if (arg == NULL)
return;
if (ds_handle == DS_INVALID_HDL) {
DBG("ps_shutdown_data_handler: DS handle no longer valid\n");
return;
}
if (msg == NULL || buflen != sizeof (platsvc_shutdown_req_t)) {
resp_msg.req_num = 0;
resp_msg.result = DOMAIN_SHUTDOWN_INVALID_MSG;
resp_msg.reason[0] = '\0';
if ((rv = ds_cap_send(ds_handle, &resp_msg,
sizeof (resp_msg))) != 0) {
cmn_err(CE_NOTE, "shutdown ds_cap_send failed (%d)",
rv);
}
return;
}
resp_msg.req_num = msg->req_num;
resp_msg.result = DOMAIN_SHUTDOWN_SUCCESS;
resp_msg.reason[0] = '\0';
if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
cmn_err(CE_NOTE, "shutdown ds_cap_send resp failed (%d)", rv);
}
/*
* Honor the ldoms manager's shutdown delay requirement.
*/
cmn_err(CE_NOTE, "shutdown requested by ldom manager, "
"system shutdown in %d minutes", MS2MIN(msg->delay));
start = gethrtime();
while (gethrtime() - start < MS2NANO(msg->delay))
;
(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
}
static void
ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
{
ds_svc_hdl_t ds_handle = ds_panic_handle;
platsvc_panic_req_t *msg = buf;
platsvc_panic_resp_t resp_msg;
uint_t rv;
if (arg == NULL)
return;
if (ds_handle == DS_INVALID_HDL) {
DBG("ps_panic_data_handler: DS handle no longer valid\n");
return;
}
if (msg == NULL || buflen != sizeof (platsvc_panic_req_t)) {
resp_msg.req_num = 0;
resp_msg.result = DOMAIN_PANIC_INVALID_MSG;
resp_msg.reason[0] = '\0';
if ((rv = ds_cap_send(ds_handle, &resp_msg,
sizeof (resp_msg))) != 0) {
cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)",
rv);
}
return;
}
resp_msg.req_num = msg->req_num;
resp_msg.result = DOMAIN_PANIC_SUCCESS;
resp_msg.reason[0] = '\0';
if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)", rv);
}
cmn_err(CE_PANIC, "Panic forced by ldom manager");
_NOTE(NOTREACHED)
}
static void
ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
{
DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
arg, ver->major, ver->minor, hdl);
if ((ds_svc_hdl_t *)arg == &ds_md_handle)
ds_md_handle = hdl;
if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
ds_shutdown_handle = hdl;
if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
ds_panic_handle = hdl;
}
static void
ps_unreg_handler(ds_cb_arg_t arg)
{
DBG("ps_unreg_handler: arg=0x%p\n", arg);
if ((ds_svc_hdl_t *)arg == &ds_md_handle)
ds_md_handle = DS_INVALID_HDL;
if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
ds_shutdown_handle = DS_INVALID_HDL;
if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
ds_panic_handle = DS_INVALID_HDL;
}