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
47uint_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 */
64static ds_svc_hdl_t	ds_md_handle;
65static ds_svc_hdl_t	ds_shutdown_handle;
66static ds_svc_hdl_t	ds_panic_handle;
67static ds_svc_hdl_t	ds_suspend_handle;
68
69static ds_ver_t		ps_vers[] = {{ 1, 0 }};
70#define	PS_NVERS	(sizeof (ps_vers) / sizeof (ps_vers[0]))
71
72static ds_capability_t ps_md_cap = {
73	"md-update",		/* svc_id */
74	ps_vers,		/* vers */
75	PS_NVERS		/* nvers */
76};
77
78static ds_capability_t ps_shutdown_cap = {
79	"domain-shutdown",	/* svc_id */
80	ps_vers,		/* vers */
81	PS_NVERS		/* nvers */
82};
83
84static ds_capability_t ps_panic_cap = {
85	"domain-panic",		/* svc_id */
86	ps_vers,		/* vers */
87	PS_NVERS		/* nvers */
88};
89
90static ds_capability_t ps_suspend_cap = {
91	"domain-suspend",	/* svc_id */
92	ps_vers,		/* vers */
93	PS_NVERS		/* nvers */
94};
95
96static void ps_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
97static void ps_unreg_handler(ds_cb_arg_t arg);
98
99static void ps_md_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
100static void ps_shutdown_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
101static void ps_panic_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
102static void ps_suspend_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
103
104static 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
111static 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
118static 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
125static 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
132static int ps_init(void);
133static 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 */
144static boolean_t ps_suspend_enabled = B_FALSE;
145
146/*
147 * Suspend service request handling
148 */
149typedef struct ps_suspend_data {
150	void		*buf;
151	size_t		buflen;
152} ps_suspend_data_t;
153
154static kmutex_t ps_suspend_mutex;
155static kcondvar_t ps_suspend_cv;
156
157static ps_suspend_data_t *ps_suspend_data = NULL;
158static boolean_t ps_suspend_thread_exit = B_FALSE;
159static kthread_t *ps_suspend_thread = NULL;
160
161static void ps_suspend_sequence(ps_suspend_data_t *data);
162static 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
173static int ps_suspend_rereg_delay = PLATSVC_SUSPEND_REREG_DELAY;
174static int ps_suspend_retry_intval = PLATSVC_SUSPEND_RETRY_INTVAL;
175
176
177static struct modlmisc modlmisc = {
178	&mod_miscops,
179	"sun4v Platform Services"
180};
181
182static struct modlinkage modlinkage = {
183	MODREV_1,
184	(void *)&modlmisc,
185	NULL
186};
187
188int
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
202int
203_info(struct modinfo *modinfop)
204{
205	return (mod_info(&modlinkage, modinfop));
206}
207
208int platsvc_allow_unload;
209
210int
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
224static int
225ps_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
288static void
289ps_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
319static void
320ps_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
370static void
371ps_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
421static void
422ps_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 */
465static void
466ps_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*/
540static void
541ps_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 */
599static void
600ps_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 */
678static void
679ps_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
704static void
705ps_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
720static void
721ps_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