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