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_sectohz(x)
  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 }