Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * sun4v Platform Services Module
     29  */
     30 
     31 #include <sys/modctl.h>
     32 #include <sys/cmn_err.h>
     33 #include <sys/machsystm.h>
     34 #include <sys/note.h>
     35 #include <sys/uadmin.h>
     36 #include <sys/ds.h>
     37 #include <sys/platsvc.h>
     38 #include <sys/ddi.h>
     39 #include <sys/suspend.h>
     40 #include <sys/proc.h>
     41 #include <sys/disp.h>
     42 
     43 /*
     44  * Debugging routines
     45  */
     46 #ifdef DEBUG
     47 uint_t ps_debug = 0x0;
     48 #define	DBG	if (ps_debug) printf
     49 #else /* DEBUG */
     50 #define	DBG	_NOTE(CONSTCOND) if (0) printf
     51 #endif /* DEBUG */
     52 
     53 /*
     54  * Time resolution conversions.
     55  */
     56 #define	MS2NANO(x)	((x) * MICROSEC)
     57 #define	MS2SEC(x)	((x) / MILLISEC)
     58 #define	MS2MIN(x)	(MS2SEC(x) / 60)
     59 #define	SEC2HZ(x)	(drv_usectohz((x) * MICROSEC))
     60 
     61 /*
     62  * Domains Services interaction
     63  */
     64 static ds_svc_hdl_t	ds_md_handle;
     65 static ds_svc_hdl_t	ds_shutdown_handle;
     66 static ds_svc_hdl_t	ds_panic_handle;
     67 static ds_svc_hdl_t	ds_suspend_handle;
     68 
     69 static ds_ver_t		ps_vers[] = {{ 1, 0 }};
     70 #define	PS_NVERS	(sizeof (ps_vers) / sizeof (ps_vers[0]))
     71 
     72 static ds_capability_t ps_md_cap = {
     73 	"md-update",		/* svc_id */
     74 	ps_vers,		/* vers */
     75 	PS_NVERS		/* nvers */
     76 };
     77 
     78 static ds_capability_t ps_shutdown_cap = {
     79 	"domain-shutdown",	/* svc_id */
     80 	ps_vers,		/* vers */
     81 	PS_NVERS		/* nvers */
     82 };
     83 
     84 static ds_capability_t ps_panic_cap = {
     85 	"domain-panic",		/* svc_id */
     86 	ps_vers,		/* vers */
     87 	PS_NVERS		/* nvers */
     88 };
     89 
     90 static ds_capability_t ps_suspend_cap = {
     91 	"domain-suspend",	/* svc_id */
     92 	ps_vers,		/* vers */
     93 	PS_NVERS		/* nvers */
     94 };
     95 
     96 static void ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
     97 static void ps_unreg_handler(ds_cb_arg_t arg);
     98 
     99 static void ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
    100 static void ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
    101 static void ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
    102 static void ps_suspend_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
    103 
    104 static ds_clnt_ops_t ps_md_ops = {
    105 	ps_reg_handler,			/* ds_reg_cb */
    106 	ps_unreg_handler,		/* ds_unreg_cb */
    107 	ps_md_data_handler,		/* ds_data_cb */
    108 	&ds_md_handle			/* cb_arg */
    109 };
    110 
    111 static ds_clnt_ops_t ps_shutdown_ops = {
    112 	ps_reg_handler,			/* ds_reg_cb */
    113 	ps_unreg_handler,		/* ds_unreg_cb */
    114 	ps_shutdown_data_handler,	/* ds_data_cb */
    115 	&ds_shutdown_handle		/* cb_arg */
    116 };
    117 
    118 static ds_clnt_ops_t ps_panic_ops = {
    119 	ps_reg_handler,			/* ds_reg_cb */
    120 	ps_unreg_handler,		/* ds_unreg_cb */
    121 	ps_panic_data_handler,		/* ds_data_cb */
    122 	&ds_panic_handle		/* cb_arg */
    123 };
    124 
    125 static ds_clnt_ops_t ps_suspend_ops = {
    126 	ps_reg_handler,			/* ds_reg_cb */
    127 	ps_unreg_handler,		/* ds_unreg_cb */
    128 	ps_suspend_data_handler,	/* ds_data_cb */
    129 	&ds_suspend_handle		/* cb_arg */
    130 };
    131 
    132 static int ps_init(void);
    133 static void ps_fini(void);
    134 
    135 /*
    136  * Power down timeout value of 5 minutes.
    137  */
    138 #define	PLATSVC_POWERDOWN_DELAY		1200
    139 
    140 /*
    141  * Set to true if OS suspend is supported. If OS suspend is not
    142  * supported, the suspend service will not be started.
    143  */
    144 static boolean_t ps_suspend_enabled = B_FALSE;
    145 
    146 /*
    147  * Suspend service request handling
    148  */
    149 typedef struct ps_suspend_data {
    150 	void		*buf;
    151 	size_t		buflen;
    152 } ps_suspend_data_t;
    153 
    154 static kmutex_t ps_suspend_mutex;
    155 static kcondvar_t ps_suspend_cv;
    156 
    157 static ps_suspend_data_t *ps_suspend_data = NULL;
    158 static boolean_t ps_suspend_thread_exit = B_FALSE;
    159 static kthread_t *ps_suspend_thread = NULL;
    160 
    161 static void ps_suspend_sequence(ps_suspend_data_t *data);
    162 static void ps_suspend_thread_func(void);
    163 
    164 /*
    165  * The DELAY timeout is the time (in seconds) to wait for the
    166  * suspend service to be re-registered after a suspend/resume
    167  * operation. The INTVAL time is the time (in seconds) to wait
    168  * between retry attempts when sending the post-suspend message
    169  * after a suspend/resume operation.
    170  */
    171 #define	PLATSVC_SUSPEND_REREG_DELAY	60
    172 #define	PLATSVC_SUSPEND_RETRY_INTVAL	1
    173 static int ps_suspend_rereg_delay = PLATSVC_SUSPEND_REREG_DELAY;
    174 static int ps_suspend_retry_intval = PLATSVC_SUSPEND_RETRY_INTVAL;
    175 
    176 
    177 static struct modlmisc modlmisc = {
    178 	&mod_miscops,
    179 	"sun4v Platform Services"
    180 };
    181 
    182 static struct modlinkage modlinkage = {
    183 	MODREV_1,
    184 	(void *)&modlmisc,
    185 	NULL
    186 };
    187 
    188 int
    189 _init(void)
    190 {
    191 	int	rv;
    192 
    193 	if ((rv = ps_init()) != 0)
    194 		return (rv);
    195 
    196 	if ((rv = mod_install(&modlinkage)) != 0)
    197 		ps_fini();
    198 
    199 	return (rv);
    200 }
    201 
    202 int
    203 _info(struct modinfo *modinfop)
    204 {
    205 	return (mod_info(&modlinkage, modinfop));
    206 }
    207 
    208 int platsvc_allow_unload;
    209 
    210 int
    211 _fini(void)
    212 {
    213 	int	status;
    214 
    215 	if (platsvc_allow_unload == 0)
    216 		return (EBUSY);
    217 
    218 	if ((status = mod_remove(&modlinkage)) == 0)
    219 		ps_fini();
    220 
    221 	return (status);
    222 }
    223 
    224 static int
    225 ps_init(void)
    226 {
    227 	int	rv;
    228 	extern int mdeg_init(void);
    229 	extern void mdeg_fini(void);
    230 
    231 	/* register with domain services framework */
    232 	rv = ds_cap_init(&ps_md_cap, &ps_md_ops);
    233 	if (rv != 0) {
    234 		cmn_err(CE_WARN, "ds_cap_init md-update failed: %d", rv);
    235 		return (rv);
    236 	}
    237 
    238 	rv = mdeg_init();
    239 	if (rv != 0) {
    240 		(void) ds_cap_fini(&ps_md_cap);
    241 		return (rv);
    242 	}
    243 
    244 	rv = ds_cap_init(&ps_shutdown_cap, &ps_shutdown_ops);
    245 	if (rv != 0) {
    246 		cmn_err(CE_WARN, "ds_cap_init domain-shutdown failed: %d", rv);
    247 		mdeg_fini();
    248 		(void) ds_cap_fini(&ps_md_cap);
    249 		return (rv);
    250 	}
    251 
    252 	rv = ds_cap_init(&ps_panic_cap, &ps_panic_ops);
    253 	if (rv != 0) {
    254 		cmn_err(CE_WARN, "ds_cap_init domain-panic failed: %d", rv);
    255 		(void) ds_cap_fini(&ps_md_cap);
    256 		mdeg_fini();
    257 		(void) ds_cap_fini(&ps_shutdown_cap);
    258 		return (rv);
    259 	}
    260 
    261 	ps_suspend_enabled = suspend_supported();
    262 
    263 	if (ps_suspend_enabled) {
    264 		mutex_init(&ps_suspend_mutex, NULL, MUTEX_DEFAULT, NULL);
    265 		cv_init(&ps_suspend_cv, NULL, CV_DEFAULT, NULL);
    266 		ps_suspend_thread_exit = B_FALSE;
    267 
    268 		rv = ds_cap_init(&ps_suspend_cap, &ps_suspend_ops);
    269 		if (rv != 0) {
    270 			cmn_err(CE_WARN, "ds_cap_init domain-suspend failed: "
    271 			    "%d", rv);
    272 			(void) ds_cap_fini(&ps_md_cap);
    273 			mdeg_fini();
    274 			(void) ds_cap_fini(&ps_shutdown_cap);
    275 			(void) ds_cap_fini(&ps_panic_cap);
    276 			mutex_destroy(&ps_suspend_mutex);
    277 			cv_destroy(&ps_suspend_cv);
    278 			return (rv);
    279 		}
    280 
    281 		ps_suspend_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
    282 		    ps_suspend_thread_func, NULL, 0, &p0, TS_RUN, minclsyspri);
    283 	}
    284 
    285 	return (0);
    286 }
    287 
    288 static void
    289 ps_fini(void)
    290 {
    291 	extern void mdeg_fini(void);
    292 
    293 	/*
    294 	 * Stop incoming requests from Zeus
    295 	 */
    296 	(void) ds_cap_fini(&ps_md_cap);
    297 	(void) ds_cap_fini(&ps_shutdown_cap);
    298 	(void) ds_cap_fini(&ps_panic_cap);
    299 
    300 	if (ps_suspend_enabled) {
    301 		(void) ds_cap_fini(&ps_suspend_cap);
    302 		if (ps_suspend_thread != NULL) {
    303 			mutex_enter(&ps_suspend_mutex);
    304 			ps_suspend_thread_exit = B_TRUE;
    305 			cv_signal(&ps_suspend_cv);
    306 			mutex_exit(&ps_suspend_mutex);
    307 
    308 			thread_join(ps_suspend_thread->t_did);
    309 			ps_suspend_thread = NULL;
    310 
    311 			mutex_destroy(&ps_suspend_mutex);
    312 			cv_destroy(&ps_suspend_cv);
    313 		}
    314 	}
    315 
    316 	mdeg_fini();
    317 }
    318 
    319 static void
    320 ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
    321 {
    322 	extern int mach_descrip_update(void);
    323 	extern void mdeg_notify_clients(void);
    324 	extern void recalc_xc_timeouts(void);
    325 
    326 	ds_svc_hdl_t		 ds_handle = ds_md_handle;
    327 	platsvc_md_update_req_t	 *msg = buf;
    328 	platsvc_md_update_resp_t resp_msg;
    329 	uint_t			 rv;
    330 
    331 	if (arg == NULL)
    332 		return;
    333 
    334 	if (ds_handle == DS_INVALID_HDL) {
    335 		DBG("ps_md_data_handler: DS handle no longer valid\n");
    336 		return;
    337 	}
    338 
    339 	if (msg == NULL || buflen != sizeof (platsvc_md_update_req_t)) {
    340 		resp_msg.req_num = 0;
    341 		resp_msg.result = MD_UPDATE_INVALID_MSG;
    342 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
    343 		    sizeof (resp_msg))) != 0) {
    344 			cmn_err(CE_NOTE, "md ds_cap_send failed (%d)", rv);
    345 		}
    346 		return;
    347 	}
    348 
    349 	DBG("MD Reload...\n");
    350 	if (mach_descrip_update()) {
    351 		cmn_err(CE_WARN, "MD reload failed\n");
    352 		return;
    353 	}
    354 
    355 	recalc_xc_timeouts();
    356 
    357 	/*
    358 	 * notify registered clients that MD has
    359 	 * been updated
    360 	 */
    361 	mdeg_notify_clients();
    362 
    363 	resp_msg.req_num = msg->req_num;
    364 	resp_msg.result = MD_UPDATE_SUCCESS;
    365 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
    366 		cmn_err(CE_NOTE, "md ds_cap_send resp failed (%d)", rv);
    367 	}
    368 }
    369 
    370 static void
    371 ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
    372 {
    373 	ds_svc_hdl_t		ds_handle = ds_shutdown_handle;
    374 	platsvc_shutdown_req_t	*msg = buf;
    375 	platsvc_shutdown_resp_t	resp_msg;
    376 	uint_t			rv;
    377 	hrtime_t		start;
    378 
    379 	if (arg == NULL)
    380 		return;
    381 
    382 	if (ds_handle == DS_INVALID_HDL) {
    383 		DBG("ps_shutdown_data_handler: DS handle no longer valid\n");
    384 		return;
    385 	}
    386 
    387 	if (msg == NULL || buflen != sizeof (platsvc_shutdown_req_t)) {
    388 		resp_msg.req_num = 0;
    389 		resp_msg.result = DOMAIN_SHUTDOWN_INVALID_MSG;
    390 		resp_msg.reason[0] = '\0';
    391 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
    392 		    sizeof (resp_msg))) != 0) {
    393 			cmn_err(CE_NOTE, "shutdown ds_cap_send failed (%d)",
    394 			    rv);
    395 		}
    396 		return;
    397 	}
    398 
    399 	resp_msg.req_num = msg->req_num;
    400 	resp_msg.result = DOMAIN_SHUTDOWN_SUCCESS;
    401 	resp_msg.reason[0] = '\0';
    402 
    403 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
    404 		cmn_err(CE_NOTE, "shutdown ds_cap_send resp failed (%d)", rv);
    405 	}
    406 
    407 	/*
    408 	 * Honor the ldoms manager's shutdown delay requirement.
    409 	 */
    410 	cmn_err(CE_NOTE, "shutdown requested by ldom manager, "
    411 	    "system shutdown in %d minutes", MS2MIN(msg->delay));
    412 
    413 	start = gethrtime();
    414 	while (gethrtime() - start < MS2NANO(msg->delay))
    415 		;
    416 
    417 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
    418 }
    419 
    420 
    421 static void
    422 ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
    423 {
    424 	ds_svc_hdl_t		ds_handle = ds_panic_handle;
    425 	platsvc_panic_req_t	*msg = buf;
    426 	platsvc_panic_resp_t	resp_msg;
    427 	uint_t			rv;
    428 
    429 	if (arg == NULL)
    430 		return;
    431 
    432 	if (ds_handle == DS_INVALID_HDL) {
    433 		DBG("ps_panic_data_handler: DS handle no longer valid\n");
    434 		return;
    435 	}
    436 
    437 	if (msg == NULL || buflen != sizeof (platsvc_panic_req_t)) {
    438 		resp_msg.req_num = 0;
    439 		resp_msg.result = DOMAIN_PANIC_INVALID_MSG;
    440 		resp_msg.reason[0] = '\0';
    441 		if ((rv = ds_cap_send(ds_handle, &resp_msg,
    442 		    sizeof (resp_msg))) != 0) {
    443 			cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)",
    444 			    rv);
    445 		}
    446 		return;
    447 	}
    448 
    449 	resp_msg.req_num = msg->req_num;
    450 	resp_msg.result = DOMAIN_PANIC_SUCCESS;
    451 	resp_msg.reason[0] = '\0';
    452 	if ((rv = ds_cap_send(ds_handle, &resp_msg, sizeof (resp_msg))) != 0) {
    453 		cmn_err(CE_NOTE, "panic ds_cap_send resp failed (%d)", rv);
    454 	}
    455 
    456 	cmn_err(CE_PANIC, "Panic forced by ldom manager");
    457 	_NOTE(NOTREACHED)
    458 }
    459 
    460 /*
    461  * Send a suspend response message. If a timeout is specified, wait
    462  * intval seconds between attempts to send the message. The timeout
    463  * and intval arguments are in seconds.
    464  */
    465 static void
    466 ps_suspend_send_response(ds_svc_hdl_t *ds_handle, uint64_t req_num,
    467     uint32_t result, uint32_t rec_result, char *reason, int timeout,
    468     int intval)
    469 {
    470 	platsvc_suspend_resp_t	*resp;
    471 	size_t			reason_length;
    472 	int			tries = 0;
    473 	int			rv = -1;
    474 	time_t			deadline;
    475 
    476 	if (reason == NULL) {
    477 		reason_length = 0;
    478 	} else {
    479 		/* Get number of non-NULL bytes */
    480 		reason_length = strnlen(reason, SUSPEND_MAX_REASON_SIZE - 1);
    481 		ASSERT(reason[reason_length] == '\0');
    482 		/* Account for NULL terminator */
    483 		reason_length++;
    484 	}
    485 
    486 	resp = (platsvc_suspend_resp_t *)
    487 	    kmem_zalloc(sizeof (platsvc_suspend_resp_t) + reason_length,
    488 	    KM_SLEEP);
    489 
    490 	resp->req_num = req_num;
    491 	resp->result = result;
    492 	resp->rec_result = rec_result;
    493 	if (reason_length > 0) {
    494 		bcopy(reason, &resp->reason, reason_length - 1);
    495 		/* Ensure NULL terminator is present */
    496 		resp->reason[reason_length] = '\0';
    497 	}
    498 
    499 	if (timeout == 0) {
    500 		tries++;
    501 		rv = ds_cap_send(*ds_handle, resp,
    502 		    sizeof (platsvc_suspend_resp_t) + reason_length);
    503 	} else {
    504 		deadline = gethrestime_sec() + timeout;
    505 		do {
    506 			ds_svc_hdl_t hdl;
    507 			/*
    508 			 * Copy the handle so we can ensure we never pass
    509 			 * an invalid handle to ds_cap_send. We don't want
    510 			 * to trigger warning messages just because the
    511 			 * service was temporarily unregistered.
    512 			 */
    513 			if ((hdl = *ds_handle) == DS_INVALID_HDL) {
    514 				delay(SEC2HZ(intval));
    515 			} else if ((rv = ds_cap_send(hdl, resp,
    516 			    sizeof (platsvc_suspend_resp_t) +
    517 			    reason_length)) != 0) {
    518 				tries++;
    519 				delay(SEC2HZ(intval));
    520 			}
    521 		} while ((rv != 0) && (gethrestime_sec() < deadline));
    522 	}
    523 
    524 	if (rv != 0) {
    525 		cmn_err(CE_NOTE, "suspend ds_cap_send resp failed (%d) "
    526 		    "sending message: %d, attempts: %d", rv, resp->result,
    527 		    tries);
    528 	}
    529 
    530 	kmem_free(resp, sizeof (platsvc_suspend_resp_t) + reason_length);
    531 }
    532 
    533 /*
    534  * Handle data coming in for the suspend service. The suspend is
    535  * sequenced by the ps_suspend_thread, but perform some checks here
    536  * to make sure that the request is a valid request message and that
    537  * a suspend operation is not already in progress.
    538  */
    539 /*ARGSUSED*/
    540 static void
    541 ps_suspend_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
    542 {
    543 	platsvc_suspend_req_t	*msg = buf;
    544 
    545 	if (arg == NULL)
    546 		return;
    547 
    548 	if (ds_suspend_handle == DS_INVALID_HDL) {
    549 		DBG("ps_suspend_data_handler: DS handle no longer valid\n");
    550 		return;
    551 	}
    552 
    553 	/* Handle invalid requests */
    554 	if (msg == NULL || buflen != sizeof (platsvc_suspend_req_t) ||
    555 	    msg->type != DOMAIN_SUSPEND_SUSPEND) {
    556 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    557 		    DOMAIN_SUSPEND_INVALID_MSG, DOMAIN_SUSPEND_REC_SUCCESS,
    558 		    NULL, 0, 0);
    559 		return;
    560 	}
    561 
    562 	/*
    563 	 * If ps_suspend_thread_exit is set, ds_cap_fini has been
    564 	 * called and we shouldn't be receving data. Handle this unexpected
    565 	 * case by returning without sending a response.
    566 	 */
    567 	if (ps_suspend_thread_exit) {
    568 		DBG("ps_suspend_data_handler: ps_suspend_thread is exiting\n");
    569 		return;
    570 	}
    571 
    572 	mutex_enter(&ps_suspend_mutex);
    573 
    574 	/* If a suspend operation is in progress, abort now */
    575 	if (ps_suspend_data != NULL) {
    576 		mutex_exit(&ps_suspend_mutex);
    577 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    578 		    DOMAIN_SUSPEND_INPROGRESS, DOMAIN_SUSPEND_REC_SUCCESS,
    579 		    NULL, 0, 0);
    580 		return;
    581 	}
    582 
    583 	ps_suspend_data = kmem_alloc(sizeof (ps_suspend_data_t), KM_SLEEP);
    584 	ps_suspend_data->buf = kmem_alloc(buflen, KM_SLEEP);
    585 	ps_suspend_data->buflen = buflen;
    586 	bcopy(buf, ps_suspend_data->buf, buflen);
    587 
    588 	cv_signal(&ps_suspend_cv);
    589 	mutex_exit(&ps_suspend_mutex);
    590 }
    591 
    592 /*
    593  * Schedule the suspend operation by calling the pre-suspend, suspend,
    594  * and post-suspend functions. When sending back response messages, we
    595  * only use a timeout for the post-suspend response because after
    596  * a resume, domain services will be re-registered and we may not
    597  * be able to send the response immediately.
    598  */
    599 static void
    600 ps_suspend_sequence(ps_suspend_data_t *data)
    601 {
    602 	platsvc_suspend_req_t	*msg;
    603 	uint32_t		rec_result;
    604 	char			*error_reason;
    605 	boolean_t		recovered = B_TRUE;
    606 	uint_t			rv;
    607 
    608 	ASSERT(data != NULL);
    609 
    610 	msg = data->buf;
    611 	error_reason = (char *)kmem_zalloc(SUSPEND_MAX_REASON_SIZE, KM_SLEEP);
    612 
    613 	/* Pre-suspend */
    614 	rv = suspend_pre(error_reason, SUSPEND_MAX_REASON_SIZE, &recovered);
    615 	if (rv != 0) {
    616 		rec_result = (recovered ? DOMAIN_SUSPEND_REC_SUCCESS :
    617 		    DOMAIN_SUSPEND_REC_FAILURE);
    618 
    619 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    620 		    DOMAIN_SUSPEND_PRE_FAILURE, rec_result, error_reason, 0, 0);
    621 
    622 		kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
    623 		return;
    624 	}
    625 
    626 	ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    627 	    DOMAIN_SUSPEND_PRE_SUCCESS, 0, NULL, 0, 0);
    628 
    629 	/* Suspend */
    630 	rv = suspend_start(error_reason, SUSPEND_MAX_REASON_SIZE);
    631 	if (rv != 0) {
    632 		rec_result = (suspend_post(NULL, 0) == 0 ?
    633 		    DOMAIN_SUSPEND_REC_SUCCESS : DOMAIN_SUSPEND_REC_FAILURE);
    634 
    635 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    636 		    DOMAIN_SUSPEND_SUSPEND_FAILURE, rec_result, error_reason,
    637 		    0, 0);
    638 
    639 		kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
    640 		return;
    641 	}
    642 
    643 	/* Post-suspend */
    644 	rv = suspend_post(error_reason, SUSPEND_MAX_REASON_SIZE);
    645 	if (rv != 0) {
    646 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    647 		    DOMAIN_SUSPEND_POST_FAILURE, 0, error_reason,
    648 		    ps_suspend_rereg_delay, ps_suspend_retry_intval);
    649 	} else {
    650 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
    651 		    DOMAIN_SUSPEND_POST_SUCCESS, 0, error_reason,
    652 		    ps_suspend_rereg_delay, ps_suspend_retry_intval);
    653 	}
    654 
    655 	kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
    656 }
    657 
    658 /*
    659  * Wait for a suspend request or for ps_suspend_thread_exit to be set.
    660  */
    661 static void
    662 ps_suspend_thread_func(void)
    663 {
    664 	mutex_enter(&ps_suspend_mutex);
    665 
    666 	while (ps_suspend_thread_exit == B_FALSE) {
    667 
    668 		if (ps_suspend_data == NULL) {
    669 			cv_wait(&ps_suspend_cv, &ps_suspend_mutex);
    670 			continue;
    671 		}
    672 
    673 		mutex_exit(&ps_suspend_mutex);
    674 		ps_suspend_sequence(ps_suspend_data);
    675 		mutex_enter(&ps_suspend_mutex);
    676 
    677 		kmem_free(ps_suspend_data->buf, ps_suspend_data->buflen);
    678 		kmem_free(ps_suspend_data, sizeof (ps_suspend_data_t));
    679 		ps_suspend_data = NULL;
    680 	}
    681 
    682 	mutex_exit(&ps_suspend_mutex);
    683 
    684 	thread_exit();
    685 }
    686 
    687 static void
    688 ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
    689 {
    690 	DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
    691 	    arg, ver->major, ver->minor, hdl);
    692 
    693 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
    694 		ds_md_handle = hdl;
    695 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
    696 		ds_shutdown_handle = hdl;
    697 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
    698 		ds_panic_handle = hdl;
    699 	if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
    700 		ds_suspend_handle = hdl;
    701 }
    702 
    703 static void
    704 ps_unreg_handler(ds_cb_arg_t arg)
    705 {
    706 	DBG("ps_unreg_handler: arg=0x%p\n", arg);
    707 
    708 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
    709 		ds_md_handle = DS_INVALID_HDL;
    710 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
    711 		ds_shutdown_handle = DS_INVALID_HDL;
    712 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
    713 		ds_panic_handle = DS_INVALID_HDL;
    714 	if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
    715 		ds_suspend_handle = DS_INVALID_HDL;
    716 }
    717