a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * CDDL HEADER START
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * The contents of this file are subject to the terms of the
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * Common Development and Distribution License (the "License").
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * You may not use this file except in compliance with the License.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * See the License for the specific language governing permissions
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * and limitations under the License.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * When distributing Covered Code, include this CDDL HEADER in each
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * If applicable, add the following below this CDDL HEADER, with the
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * fields enclosed by brackets "[]" replaced with your own identifying
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * information: Portions Copyright [yyyy] [name of copyright owner]
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * CDDL HEADER END
bec42f4bc1a5f12e6920d2a698474e565b6d68d7Mary Beale * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * A CPR derivative specifically for starfire/starcat
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * X86 doesn't make use of the quiesce interfaces, it's kept for simplicity.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liuextern void e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liuextern void e_ddi_exit_driver_list(struct devnames *dnp, int listcnt);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic int dr_check_dip(dev_info_t *dip, void *arg, uint_t ref);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic int dr_resolve_devname(dev_info_t *dip, char *buffer,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic sbd_error_t *drerr_int(int e_code, uint64_t *arr, int idx,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic int dr_add_int(uint64_t *arr, int idx, int len,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * NOTE: states used internally by dr_suspend and dr_resume
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * This hack will go away before RTI. Just for testing.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * List of drivers to bypass when performing a suspend.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu#define SKIP_SYNC /* bypass sync ops in dr_suspend */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * dr_skip_user_threads is used to control if user threads should
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * be suspended. If dr_skip_user_threads is true, the rest of the
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * flags are not used; if it is false, dr_check_user_stop_result
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * will be used to control whether or not we need to check suspend
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * result, and dr_allow_blocked_threads will be used to control
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * whether or not we allow suspend to continue if there are blocked
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * threads. We allow all combinations of dr_check_user_stop_result
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * and dr_allow_block_threads, even though it might not make much
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * sense to not allow block threads when we don't even check stop
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic int dr_skip_user_threads = 0; /* default to FALSE */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic int dr_check_user_stop_result = 1; /* default to TRUE */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liustatic int dr_allow_blocked_threads = 1; /* default to TRUE */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR))
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * now the general case
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_QR("dr_is_unsafe_major: invalid major # %d\n", major);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* check the bypass list */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudr_resolve_devname(dev_info_t *dip, char *buffer, char *alias)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu return (-1);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if ((devmajor = ddi_name_to_major(aka)) != DDI_MAJOR_T_NONE)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu/* ARGSUSED */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudr_check_dip(dev_info_t *dip, void *arg, uint_t ref)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_QR("\n %s (major# %d) is referenced(%u)\n", dname,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (dr_is_unsafe_major(major) && i_ddi_devi_attached(dip)) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_QR("\n %s (major# %d) not hotpluggable\n", dname,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu uint64_t *arr, int *idx, int len, int *refcount_non_gldv3)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu (void) e_ddi_branch_referenced(dip, dr_check_dip, &bref);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * The "dip" argument's parent (if it exists) must be held busy.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * If dip is the root node, it has no siblings and it is
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * always held. If dip is not the root node, dr_suspend_devices()
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * will be invoked with the parent held busy.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (dr_suspend_devices(ddi_get_child(dip), srh)) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (d_alias[0] != 0) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu prom_printf("\tsuspending %s@%s\n", dname, d_info);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu srh->sr_err_idx, DR_MAX_ERR_INT, (uint64_t)major);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* attach in reverse device tree order */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu while (next != last && dip != srh->sr_failed_dip) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* release hold acquired in dr_suspend_devices() */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (d_alias[0] != 0) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * Print a console warning,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * set an e_code of ESBD_RESUME,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * and save the driver major
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * number in the e_rsc.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* Hold parent busy while walking its children */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * True if thread is virtually stopped. Similar to CPR_VSTOPPED
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * but from DR point of view. These user threads are waiting in
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * the kernel. Once they complete in the kernel, they will process
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * the stop signal and stop.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu/* ARGSUSED */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu extern void add_one_utstop();
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu extern void utstop_init(void);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* we need to try a few times to get past fork, etc. */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (count = 0; count < DR_UTSTOP_RETRY; count++) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* walk the entire threadlist */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* handle kernel threads separately */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* add another reason to stop this thread */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* grab thread if needed */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* let everything catch up */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* now, walk the threadlist again to see if we are done */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* handle kernel threads separately */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * If this thread didn't stop, and we don't allow
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * unstopped blocked threads, bail.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * save the pid for later reporting
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu "failed to stop thread: "
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu "process=%s, pid=%d",
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu "process=%s, pid=%d, t_id=0x%p, "
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu "t_state=0x%x, t_proc_flag=0x%x, "
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu "t_schedflag=0x%x\n",
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* were all the threads stopped? */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* were we unable to stop all threads after a few tries? */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* walk all threads and release them */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* skip kernel threads */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* back on the runq */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* only user threads */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* add a bit of delay */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * Prevent false alarm in tod_validate() due to tod
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * value change between suspend and resume
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * This should only be called if drmach_suspend_last()
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * was called and state transitioned to DR_SRSTATE_FULL
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * to prevent resume attempts on device instances that
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * were not previously suspended.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* FALLTHROUGH */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * resume drivers
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* no parent dip to hold busy */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu (srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * resume the lock manager
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* FALLTHROUGH */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * finally, resume user threads
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* FALLTHROUGH */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * let those who care know that we've just resumed
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu prom_printf("\nDR: suspending user threads...\n");
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) &&
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * Since the root node can never go away, it
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * doesn't have to be held.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu prom_printf("\nDR: dr_suspend invoked with force flag\n");
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * This sync swap out all user pages
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * special treatment for lock manager
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * sync the file system in case we never make it back
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * now suspend drivers
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* No parent to hold busy */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu (srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND,
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * finally, grab all cpus
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_ALL("%s: dr_suspend() failed, err = 0x%x\n", f, err);
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR;
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu PR_ALL("Could not resume devices (major #): %s\n",
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * Add a new integer value to the end of an array. Don't allow duplicates to
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * appear in the array, and don't allow the array to overflow. Return the new
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * total number of entries in the array.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudr_add_int(uint64_t *arr, int idx, int len, uint64_t val)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu for (i = 0; i < idx; i++) {
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * Construct an sbd_error_t featuring a string representation of an array of
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * integers as its e_rsc.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liudrerr_int(int e_code, uint64_t *arr, int idx, int majors)
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * This is the total working area of the buffer. It must be computed
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * as the size of 'buf', minus reserved space for the null terminator
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu * and the ellipsis string.
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* Construct a string representation of the array values */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu n = snprintf(&buf[buf_idx], buf_avail, "%" PRIu64 ", ",
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* An ellipsis gets appended when no more values fit */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* If all the contents fit, remove the trailing comma */
a31148363f598def767ac48c5d82e1572e44b935Gerry Liu /* Return an sbd_error_t with the buffer and e_code */