xref: /illumos-gate/usr/src/uts/sun4v/io/platsvc.c (revision d0698e0d179f97729cacdbc2f13446a6b0a3f22a)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * sun4v Platform Services Module
28  */
29 
30 #include <sys/modctl.h>
31 #include <sys/cmn_err.h>
32 #include <sys/machsystm.h>
33 #include <sys/note.h>
34 #include <sys/uadmin.h>
35 #include <sys/ds.h>
36 #include <sys/platsvc.h>
37 #include <sys/ddi.h>
38 #include <sys/suspend.h>
39 #include <sys/proc.h>
40 #include <sys/disp.h>
41 #include <sys/drctl.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 = 0;
607 	int			dr_block;
608 
609 	ASSERT(data != NULL);
610 
611 	msg = data->buf;
612 	error_reason = (char *)kmem_zalloc(SUSPEND_MAX_REASON_SIZE, KM_SLEEP);
613 
614 	/*
615 	 * Abort the suspend if a DR operation is in progress. Otherwise,
616 	 * continue whilst blocking any new DR operations.
617 	 */
618 	if ((dr_block = drctl_tryblock()) == 0) {
619 		/* Pre-suspend */
620 		rv = suspend_pre(error_reason, SUSPEND_MAX_REASON_SIZE,
621 		    &recovered);
622 	} else {
623 		/* A DR operation is in progress */
624 		(void) strncpy(error_reason, DOMAIN_SUSPEND_DR_ERROR_STR,
625 		    SUSPEND_MAX_REASON_SIZE);
626 	}
627 
628 	if (dr_block != 0 || rv != 0) {
629 		rec_result = (recovered ? DOMAIN_SUSPEND_REC_SUCCESS :
630 		    DOMAIN_SUSPEND_REC_FAILURE);
631 
632 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
633 		    DOMAIN_SUSPEND_PRE_FAILURE, rec_result, error_reason, 0, 0);
634 
635 		if (dr_block == 0)
636 			drctl_unblock();
637 		kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
638 		return;
639 	}
640 
641 	ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
642 	    DOMAIN_SUSPEND_PRE_SUCCESS, 0, NULL, 0, 0);
643 
644 	/* Suspend */
645 	rv = suspend_start(error_reason, SUSPEND_MAX_REASON_SIZE);
646 	if (rv != 0) {
647 		rec_result = (suspend_post(NULL, 0) == 0 ?
648 		    DOMAIN_SUSPEND_REC_SUCCESS : DOMAIN_SUSPEND_REC_FAILURE);
649 
650 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
651 		    DOMAIN_SUSPEND_SUSPEND_FAILURE, rec_result, error_reason,
652 		    0, 0);
653 
654 		drctl_unblock();
655 		kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
656 		return;
657 	}
658 
659 	/* Post-suspend */
660 	rv = suspend_post(error_reason, SUSPEND_MAX_REASON_SIZE);
661 	if (rv != 0) {
662 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
663 		    DOMAIN_SUSPEND_POST_FAILURE, 0, error_reason,
664 		    ps_suspend_rereg_delay, ps_suspend_retry_intval);
665 	} else {
666 		ps_suspend_send_response(&ds_suspend_handle, msg->req_num,
667 		    DOMAIN_SUSPEND_POST_SUCCESS, 0, error_reason,
668 		    ps_suspend_rereg_delay, ps_suspend_retry_intval);
669 	}
670 
671 	drctl_unblock();
672 	kmem_free(error_reason, SUSPEND_MAX_REASON_SIZE);
673 }
674 
675 /*
676  * Wait for a suspend request or for ps_suspend_thread_exit to be set.
677  */
678 static void
679 ps_suspend_thread_func(void)
680 {
681 	mutex_enter(&ps_suspend_mutex);
682 
683 	while (ps_suspend_thread_exit == B_FALSE) {
684 
685 		if (ps_suspend_data == NULL) {
686 			cv_wait(&ps_suspend_cv, &ps_suspend_mutex);
687 			continue;
688 		}
689 
690 		mutex_exit(&ps_suspend_mutex);
691 		ps_suspend_sequence(ps_suspend_data);
692 		mutex_enter(&ps_suspend_mutex);
693 
694 		kmem_free(ps_suspend_data->buf, ps_suspend_data->buflen);
695 		kmem_free(ps_suspend_data, sizeof (ps_suspend_data_t));
696 		ps_suspend_data = NULL;
697 	}
698 
699 	mutex_exit(&ps_suspend_mutex);
700 
701 	thread_exit();
702 }
703 
704 static void
705 ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
706 {
707 	DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
708 	    arg, ver->major, ver->minor, hdl);
709 
710 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
711 		ds_md_handle = hdl;
712 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
713 		ds_shutdown_handle = hdl;
714 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
715 		ds_panic_handle = hdl;
716 	if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
717 		ds_suspend_handle = hdl;
718 }
719 
720 static void
721 ps_unreg_handler(ds_cb_arg_t arg)
722 {
723 	DBG("ps_unreg_handler: arg=0x%p\n", arg);
724 
725 	if ((ds_svc_hdl_t *)arg == &ds_md_handle)
726 		ds_md_handle = DS_INVALID_HDL;
727 	if ((ds_svc_hdl_t *)arg == &ds_shutdown_handle)
728 		ds_shutdown_handle = DS_INVALID_HDL;
729 	if ((ds_svc_hdl_t *)arg == &ds_panic_handle)
730 		ds_panic_handle = DS_INVALID_HDL;
731 	if ((ds_svc_hdl_t *)arg == &ds_suspend_handle)
732 		ds_suspend_handle = DS_INVALID_HDL;
733 }
734