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.
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.
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]
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
27 * sun4v Platform Services Module
30 #include <sys/modctl.h>
31 #include <sys/cmn_err.h>
32 #include <sys/machsystm.h>
34 #include <sys/uadmin.h>
36 #include <sys/platsvc.h>
38 #include <sys/suspend.h>
41 #include <sys/drctl.h>
47 uint_t ps_debug
= 0x0;
48 #define DBG if (ps_debug) printf
50 #define DBG _NOTE(CONSTCOND) if (0) printf
54 * Time resolution conversions.
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))
62 * Domains Services interaction
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
;
69 static ds_ver_t ps_vers
[] = {{ 1, 0 }};
70 #define PS_NVERS (sizeof (ps_vers) / sizeof (ps_vers[0]))
72 static ds_capability_t ps_md_cap
= {
73 "md-update", /* svc_id */
78 static ds_capability_t ps_shutdown_cap
= {
79 "domain-shutdown", /* svc_id */
84 static ds_capability_t ps_panic_cap
= {
85 "domain-panic", /* svc_id */
90 static ds_capability_t ps_suspend_cap
= {
91 "domain-suspend", /* svc_id */
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
);
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
);
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 */
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 */
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 */
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 */
132 static int ps_init(void);
133 static void ps_fini(void);
136 * Power down timeout value of 5 minutes.
138 #define PLATSVC_POWERDOWN_DELAY 1200
141 * Set to true if OS suspend is supported. If OS suspend is not
142 * supported, the suspend service will not be started.
144 static boolean_t ps_suspend_enabled
= B_FALSE
;
147 * Suspend service request handling
149 typedef struct ps_suspend_data
{
154 static kmutex_t ps_suspend_mutex
;
155 static kcondvar_t ps_suspend_cv
;
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
;
161 static void ps_suspend_sequence(ps_suspend_data_t
*data
);
162 static void ps_suspend_thread_func(void);
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.
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
;
177 static struct modlmisc modlmisc
= {
179 "sun4v Platform Services"
182 static struct modlinkage modlinkage
= {
193 if ((rv
= ps_init()) != 0)
196 if ((rv
= mod_install(&modlinkage
)) != 0)
203 _info(struct modinfo
*modinfop
)
205 return (mod_info(&modlinkage
, modinfop
));
208 int platsvc_allow_unload
;
215 if (platsvc_allow_unload
== 0)
218 if ((status
= mod_remove(&modlinkage
)) == 0)
228 extern int mdeg_init(void);
229 extern void mdeg_fini(void);
231 /* register with domain services framework */
232 rv
= ds_cap_init(&ps_md_cap
, &ps_md_ops
);
234 cmn_err(CE_WARN
, "ds_cap_init md-update failed: %d", rv
);
240 (void) ds_cap_fini(&ps_md_cap
);
244 rv
= ds_cap_init(&ps_shutdown_cap
, &ps_shutdown_ops
);
246 cmn_err(CE_WARN
, "ds_cap_init domain-shutdown failed: %d", rv
);
248 (void) ds_cap_fini(&ps_md_cap
);
252 rv
= ds_cap_init(&ps_panic_cap
, &ps_panic_ops
);
254 cmn_err(CE_WARN
, "ds_cap_init domain-panic failed: %d", rv
);
255 (void) ds_cap_fini(&ps_md_cap
);
257 (void) ds_cap_fini(&ps_shutdown_cap
);
261 ps_suspend_enabled
= suspend_supported();
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
;
268 rv
= ds_cap_init(&ps_suspend_cap
, &ps_suspend_ops
);
270 cmn_err(CE_WARN
, "ds_cap_init domain-suspend failed: "
272 (void) ds_cap_fini(&ps_md_cap
);
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
);
281 ps_suspend_thread
= thread_create(NULL
, 2 * DEFAULTSTKSZ
,
282 ps_suspend_thread_func
, NULL
, 0, &p0
, TS_RUN
, minclsyspri
);
291 extern void mdeg_fini(void);
294 * Stop incoming requests from Zeus
296 (void) ds_cap_fini(&ps_md_cap
);
297 (void) ds_cap_fini(&ps_shutdown_cap
);
298 (void) ds_cap_fini(&ps_panic_cap
);
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
);
308 thread_join(ps_suspend_thread
->t_did
);
309 ps_suspend_thread
= NULL
;
311 mutex_destroy(&ps_suspend_mutex
);
312 cv_destroy(&ps_suspend_cv
);
320 ps_md_data_handler(ds_cb_arg_t arg
, void *buf
, size_t buflen
)
322 extern int mach_descrip_update(void);
323 extern void mdeg_notify_clients(void);
324 extern void recalc_xc_timeouts(void);
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
;
334 if (ds_handle
== DS_INVALID_HDL
) {
335 DBG("ps_md_data_handler: DS handle no longer valid\n");
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
);
349 DBG("MD Reload...\n");
350 if (mach_descrip_update()) {
351 cmn_err(CE_WARN
, "MD reload failed\n");
355 recalc_xc_timeouts();
358 * notify registered clients that MD has
361 mdeg_notify_clients();
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
);
371 ps_shutdown_data_handler(ds_cb_arg_t arg
, void *buf
, size_t buflen
)
373 ds_svc_hdl_t ds_handle
= ds_shutdown_handle
;
374 platsvc_shutdown_req_t
*msg
= buf
;
375 platsvc_shutdown_resp_t resp_msg
;
382 if (ds_handle
== DS_INVALID_HDL
) {
383 DBG("ps_shutdown_data_handler: DS handle no longer valid\n");
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)",
399 resp_msg
.req_num
= msg
->req_num
;
400 resp_msg
.result
= DOMAIN_SHUTDOWN_SUCCESS
;
401 resp_msg
.reason
[0] = '\0';
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
);
408 * Honor the ldoms manager's shutdown delay requirement.
410 cmn_err(CE_NOTE
, "shutdown requested by ldom manager, "
411 "system shutdown in %d minutes", MS2MIN(msg
->delay
));
414 while (gethrtime() - start
< MS2NANO(msg
->delay
))
417 (void) kadmin(A_SHUTDOWN
, AD_POWEROFF
, NULL
, kcred
);
422 ps_panic_data_handler(ds_cb_arg_t arg
, void *buf
, size_t buflen
)
424 ds_svc_hdl_t ds_handle
= ds_panic_handle
;
425 platsvc_panic_req_t
*msg
= buf
;
426 platsvc_panic_resp_t resp_msg
;
432 if (ds_handle
== DS_INVALID_HDL
) {
433 DBG("ps_panic_data_handler: DS handle no longer valid\n");
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)",
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
);
456 cmn_err(CE_PANIC
, "Panic forced by ldom manager");
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.
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
,
470 platsvc_suspend_resp_t
*resp
;
471 size_t reason_length
;
476 if (reason
== NULL
) {
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 */
486 resp
= (platsvc_suspend_resp_t
*)
487 kmem_zalloc(sizeof (platsvc_suspend_resp_t
) + reason_length
,
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';
501 rv
= ds_cap_send(*ds_handle
, resp
,
502 sizeof (platsvc_suspend_resp_t
) + reason_length
);
504 deadline
= gethrestime_sec() + timeout
;
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.
513 if ((hdl
= *ds_handle
) == DS_INVALID_HDL
) {
515 } else if ((rv
= ds_cap_send(hdl
, resp
,
516 sizeof (platsvc_suspend_resp_t
) +
517 reason_length
)) != 0) {
521 } while ((rv
!= 0) && (gethrestime_sec() < deadline
));
525 cmn_err(CE_NOTE
, "suspend ds_cap_send resp failed (%d) "
526 "sending message: %d, attempts: %d", rv
, resp
->result
,
530 kmem_free(resp
, sizeof (platsvc_suspend_resp_t
) + reason_length
);
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.
541 ps_suspend_data_handler(ds_cb_arg_t arg
, void *buf
, size_t buflen
)
543 platsvc_suspend_req_t
*msg
= buf
;
548 if (ds_suspend_handle
== DS_INVALID_HDL
) {
549 DBG("ps_suspend_data_handler: DS handle no longer valid\n");
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
,
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.
567 if (ps_suspend_thread_exit
) {
568 DBG("ps_suspend_data_handler: ps_suspend_thread is exiting\n");
572 mutex_enter(&ps_suspend_mutex
);
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
,
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
);
588 cv_signal(&ps_suspend_cv
);
589 mutex_exit(&ps_suspend_mutex
);
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.
600 ps_suspend_sequence(ps_suspend_data_t
*data
)
602 platsvc_suspend_req_t
*msg
;
605 boolean_t recovered
= B_TRUE
;
609 ASSERT(data
!= NULL
);
612 error_reason
= kmem_zalloc(SUSPEND_MAX_REASON_SIZE
, KM_SLEEP
);
615 * Abort the suspend if a DR operation is in progress. Otherwise,
616 * continue whilst blocking any new DR operations.
618 if ((dr_block
= drctl_tryblock()) == 0) {
620 rv
= suspend_pre(error_reason
, SUSPEND_MAX_REASON_SIZE
,
623 /* A DR operation is in progress */
624 (void) strncpy(error_reason
, DOMAIN_SUSPEND_DR_ERROR_STR
,
625 SUSPEND_MAX_REASON_SIZE
);
628 if (dr_block
!= 0 || rv
!= 0) {
629 rec_result
= (recovered
? DOMAIN_SUSPEND_REC_SUCCESS
:
630 DOMAIN_SUSPEND_REC_FAILURE
);
632 ps_suspend_send_response(&ds_suspend_handle
, msg
->req_num
,
633 DOMAIN_SUSPEND_PRE_FAILURE
, rec_result
, error_reason
, 0, 0);
637 kmem_free(error_reason
, SUSPEND_MAX_REASON_SIZE
);
641 ps_suspend_send_response(&ds_suspend_handle
, msg
->req_num
,
642 DOMAIN_SUSPEND_PRE_SUCCESS
, 0, NULL
, 0, 0);
645 rv
= suspend_start(error_reason
, SUSPEND_MAX_REASON_SIZE
);
647 rec_result
= (suspend_post(NULL
, 0) == 0 ?
648 DOMAIN_SUSPEND_REC_SUCCESS
: DOMAIN_SUSPEND_REC_FAILURE
);
650 ps_suspend_send_response(&ds_suspend_handle
, msg
->req_num
,
651 DOMAIN_SUSPEND_SUSPEND_FAILURE
, rec_result
, error_reason
,
655 kmem_free(error_reason
, SUSPEND_MAX_REASON_SIZE
);
660 rv
= suspend_post(error_reason
, SUSPEND_MAX_REASON_SIZE
);
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
);
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
);
672 kmem_free(error_reason
, SUSPEND_MAX_REASON_SIZE
);
676 * Wait for a suspend request or for ps_suspend_thread_exit to be set.
679 ps_suspend_thread_func(void)
681 mutex_enter(&ps_suspend_mutex
);
683 while (ps_suspend_thread_exit
== B_FALSE
) {
685 if (ps_suspend_data
== NULL
) {
686 cv_wait(&ps_suspend_cv
, &ps_suspend_mutex
);
690 mutex_exit(&ps_suspend_mutex
);
691 ps_suspend_sequence(ps_suspend_data
);
692 mutex_enter(&ps_suspend_mutex
);
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
;
699 mutex_exit(&ps_suspend_mutex
);
705 ps_reg_handler(ds_cb_arg_t arg
, ds_ver_t
*ver
, ds_svc_hdl_t hdl
)
707 DBG("ps_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
708 arg
, ver
->major
, ver
->minor
, hdl
);
710 if ((ds_svc_hdl_t
*)arg
== &ds_md_handle
)
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
;
721 ps_unreg_handler(ds_cb_arg_t arg
)
723 DBG("ps_unreg_handler: arg=0x%p\n", arg
);
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
;