1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * A CPR derivative specifically for starfire/starcat 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <sys/types.h> 34*0Sstevel@tonic-gate #include <sys/systm.h> 35*0Sstevel@tonic-gate #include <sys/machparam.h> 36*0Sstevel@tonic-gate #include <sys/machsystm.h> 37*0Sstevel@tonic-gate #include <sys/ddi.h> 38*0Sstevel@tonic-gate #define SUNDDI_IMPL 39*0Sstevel@tonic-gate #include <sys/sunddi.h> 40*0Sstevel@tonic-gate #include <sys/sunndi.h> 41*0Sstevel@tonic-gate #include <sys/devctl.h> 42*0Sstevel@tonic-gate #include <sys/time.h> 43*0Sstevel@tonic-gate #include <sys/kmem.h> 44*0Sstevel@tonic-gate #include <nfs/lm.h> 45*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 46*0Sstevel@tonic-gate #include <sys/ndi_impldefs.h> 47*0Sstevel@tonic-gate #include <sys/obpdefs.h> 48*0Sstevel@tonic-gate #include <sys/cmn_err.h> 49*0Sstevel@tonic-gate #include <sys/debug.h> 50*0Sstevel@tonic-gate #include <sys/errno.h> 51*0Sstevel@tonic-gate #include <sys/callb.h> 52*0Sstevel@tonic-gate #include <sys/clock.h> 53*0Sstevel@tonic-gate #include <sys/x_call.h> 54*0Sstevel@tonic-gate #include <sys/cpuvar.h> 55*0Sstevel@tonic-gate #include <sys/epm.h> 56*0Sstevel@tonic-gate #include <sys/vfs.h> 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate #include <sys/cpu_sgnblk_defs.h> 59*0Sstevel@tonic-gate #include <sys/dr.h> 60*0Sstevel@tonic-gate #include <sys/dr_util.h> 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate #include <sys/promif.h> 63*0Sstevel@tonic-gate #include <sys/conf.h> 64*0Sstevel@tonic-gate #include <sys/cyclic.h> 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate extern void e_ddi_enter_driver_list(struct devnames *dnp, int *listcnt); 67*0Sstevel@tonic-gate extern void e_ddi_exit_driver_list(struct devnames *dnp, int listcnt); 68*0Sstevel@tonic-gate extern int is_pseudo_device(dev_info_t *dip); 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate extern kmutex_t cpu_lock; 71*0Sstevel@tonic-gate extern dr_unsafe_devs_t dr_unsafe_devs; 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate static int dr_is_real_device(dev_info_t *dip); 74*0Sstevel@tonic-gate static int dr_is_unsafe_major(major_t major); 75*0Sstevel@tonic-gate static int dr_bypass_device(char *dname); 76*0Sstevel@tonic-gate static int dr_check_dip(dev_info_t *dip, void *arg, uint_t ref); 77*0Sstevel@tonic-gate static int dr_resolve_devname(dev_info_t *dip, char *buffer, 78*0Sstevel@tonic-gate char *alias); 79*0Sstevel@tonic-gate static sbd_error_t *drerr_int(int e_code, uint64_t *arr, int idx, 80*0Sstevel@tonic-gate int majors); 81*0Sstevel@tonic-gate static int dr_add_int(uint64_t *arr, int idx, int len, 82*0Sstevel@tonic-gate uint64_t val); 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate int dr_pt_test_suspend(dr_handle_t *hp); 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate /* 87*0Sstevel@tonic-gate * dr_quiesce.c interface 88*0Sstevel@tonic-gate * NOTE: states used internally by dr_suspend and dr_resume 89*0Sstevel@tonic-gate */ 90*0Sstevel@tonic-gate typedef enum dr_suspend_state { 91*0Sstevel@tonic-gate DR_SRSTATE_BEGIN = 0, 92*0Sstevel@tonic-gate DR_SRSTATE_USER, 93*0Sstevel@tonic-gate DR_SRSTATE_DAEMON, 94*0Sstevel@tonic-gate DR_SRSTATE_DRIVER, 95*0Sstevel@tonic-gate DR_SRSTATE_FULL 96*0Sstevel@tonic-gate } suspend_state_t; 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate struct dr_sr_handle { 99*0Sstevel@tonic-gate dr_handle_t *sr_dr_handlep; 100*0Sstevel@tonic-gate dev_info_t *sr_failed_dip; 101*0Sstevel@tonic-gate suspend_state_t sr_suspend_state; 102*0Sstevel@tonic-gate uint_t sr_flags; 103*0Sstevel@tonic-gate uint64_t sr_err_ints[DR_MAX_ERR_INT]; 104*0Sstevel@tonic-gate int sr_err_idx; 105*0Sstevel@tonic-gate }; 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate #define SR_FLAG_WATCHDOG 0x1 108*0Sstevel@tonic-gate 109*0Sstevel@tonic-gate /* 110*0Sstevel@tonic-gate * XXX 111*0Sstevel@tonic-gate * This hack will go away before RTI. Just for testing. 112*0Sstevel@tonic-gate * List of drivers to bypass when performing a suspend. 113*0Sstevel@tonic-gate */ 114*0Sstevel@tonic-gate static char *dr_bypass_list[] = { 115*0Sstevel@tonic-gate "" 116*0Sstevel@tonic-gate }; 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate 119*0Sstevel@tonic-gate static int dr_skip_kernel_threads = 1; /* "TRUE" */ 120*0Sstevel@tonic-gate #define SKIP_SYNC /* bypass sync ops in dr_suspend */ 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate /* 123*0Sstevel@tonic-gate * dr_skip_user_threads is used to control if user threads should 124*0Sstevel@tonic-gate * be suspended. If dr_skip_user_threads is true, the rest of the 125*0Sstevel@tonic-gate * flags are not used; if it is false, dr_check_user_stop_result 126*0Sstevel@tonic-gate * will be used to control whether or not we need to check suspend 127*0Sstevel@tonic-gate * result, and dr_allow_blocked_threads will be used to control 128*0Sstevel@tonic-gate * whether or not we allow suspend to continue if there are blocked 129*0Sstevel@tonic-gate * threads. We allow all combinations of dr_check_user_stop_result 130*0Sstevel@tonic-gate * and dr_allow_block_threads, even though it might not make much 131*0Sstevel@tonic-gate * sense to not allow block threads when we don't even check stop 132*0Sstevel@tonic-gate * result. 133*0Sstevel@tonic-gate */ 134*0Sstevel@tonic-gate static int dr_skip_user_threads = 0; /* default to FALSE */ 135*0Sstevel@tonic-gate static int dr_check_user_stop_result = 1; /* default to TRUE */ 136*0Sstevel@tonic-gate static int dr_allow_blocked_threads = 1; /* default to TRUE */ 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate #define DR_CPU_LOOP_MSEC 1000 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate static void 141*0Sstevel@tonic-gate dr_stop_intr(void) 142*0Sstevel@tonic-gate { 143*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate kpreempt_disable(); 146*0Sstevel@tonic-gate cyclic_suspend(); 147*0Sstevel@tonic-gate } 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate static void 150*0Sstevel@tonic-gate dr_enable_intr(void) 151*0Sstevel@tonic-gate { 152*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate cyclic_resume(); 155*0Sstevel@tonic-gate kpreempt_enable(); 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate dr_sr_handle_t * 159*0Sstevel@tonic-gate dr_get_sr_handle(dr_handle_t *hp) 160*0Sstevel@tonic-gate { 161*0Sstevel@tonic-gate dr_sr_handle_t *srh; 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate srh = GETSTRUCT(dr_sr_handle_t, 1); 164*0Sstevel@tonic-gate srh->sr_dr_handlep = hp; 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate return (srh); 167*0Sstevel@tonic-gate } 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate void 170*0Sstevel@tonic-gate dr_release_sr_handle(dr_sr_handle_t *srh) 171*0Sstevel@tonic-gate { 172*0Sstevel@tonic-gate ASSERT(srh->sr_failed_dip == NULL); 173*0Sstevel@tonic-gate FREESTRUCT(srh, dr_sr_handle_t, 1); 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate static int 177*0Sstevel@tonic-gate dr_is_real_device(dev_info_t *dip) 178*0Sstevel@tonic-gate { 179*0Sstevel@tonic-gate struct regspec *regbuf = NULL; 180*0Sstevel@tonic-gate int length = 0; 181*0Sstevel@tonic-gate int rc; 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate if (ddi_get_driver(dip) == NULL) 184*0Sstevel@tonic-gate return (0); 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR)) 187*0Sstevel@tonic-gate return (1); 188*0Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NO_SR) 189*0Sstevel@tonic-gate return (0); 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate /* 192*0Sstevel@tonic-gate * now the general case 193*0Sstevel@tonic-gate */ 194*0Sstevel@tonic-gate rc = ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "reg", 195*0Sstevel@tonic-gate (caddr_t)®buf, &length); 196*0Sstevel@tonic-gate ASSERT(rc != DDI_PROP_NO_MEMORY); 197*0Sstevel@tonic-gate if (rc != DDI_PROP_SUCCESS) { 198*0Sstevel@tonic-gate return (0); 199*0Sstevel@tonic-gate } else { 200*0Sstevel@tonic-gate if ((length > 0) && (regbuf != NULL)) 201*0Sstevel@tonic-gate kmem_free(regbuf, length); 202*0Sstevel@tonic-gate return (1); 203*0Sstevel@tonic-gate } 204*0Sstevel@tonic-gate } 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate static int 207*0Sstevel@tonic-gate dr_is_unsafe_major(major_t major) 208*0Sstevel@tonic-gate { 209*0Sstevel@tonic-gate char *dname, **cpp; 210*0Sstevel@tonic-gate int i, ndevs; 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate if ((dname = ddi_major_to_name(major)) == NULL) { 213*0Sstevel@tonic-gate PR_QR("dr_is_unsafe_major: invalid major # %d\n", major); 214*0Sstevel@tonic-gate return (0); 215*0Sstevel@tonic-gate } 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate ndevs = dr_unsafe_devs.ndevs; 218*0Sstevel@tonic-gate for (i = 0, cpp = dr_unsafe_devs.devnames; i < ndevs; i++) { 219*0Sstevel@tonic-gate if (strcmp(dname, *cpp++) == 0) 220*0Sstevel@tonic-gate return (1); 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate return (0); 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate static int 226*0Sstevel@tonic-gate dr_bypass_device(char *dname) 227*0Sstevel@tonic-gate { 228*0Sstevel@tonic-gate int i; 229*0Sstevel@tonic-gate char **lname; 230*0Sstevel@tonic-gate /* check the bypass list */ 231*0Sstevel@tonic-gate for (i = 0, lname = &dr_bypass_list[i]; **lname != '\0'; lname++) { 232*0Sstevel@tonic-gate if (strcmp(dname, dr_bypass_list[i++]) == 0) 233*0Sstevel@tonic-gate return (1); 234*0Sstevel@tonic-gate } 235*0Sstevel@tonic-gate return (0); 236*0Sstevel@tonic-gate } 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gate static int 239*0Sstevel@tonic-gate dr_resolve_devname(dev_info_t *dip, char *buffer, char *alias) 240*0Sstevel@tonic-gate { 241*0Sstevel@tonic-gate major_t devmajor; 242*0Sstevel@tonic-gate char *aka, *name; 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate *buffer = *alias = 0; 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate if (dip == NULL) 247*0Sstevel@tonic-gate return (-1); 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate if ((name = ddi_get_name(dip)) == NULL) 250*0Sstevel@tonic-gate name = "<null name>"; 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate aka = name; 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate if ((devmajor = ddi_name_to_major(aka)) != -1) 255*0Sstevel@tonic-gate aka = ddi_major_to_name(devmajor); 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate strcpy(buffer, name); 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate if (strcmp(name, aka)) 260*0Sstevel@tonic-gate strcpy(alias, aka); 261*0Sstevel@tonic-gate else 262*0Sstevel@tonic-gate *alias = 0; 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate return (0); 265*0Sstevel@tonic-gate } 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate struct dr_ref { 268*0Sstevel@tonic-gate int *refcount; 269*0Sstevel@tonic-gate uint64_t *arr; 270*0Sstevel@tonic-gate int *idx; 271*0Sstevel@tonic-gate int len; 272*0Sstevel@tonic-gate }; 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate /* ARGSUSED */ 275*0Sstevel@tonic-gate static int 276*0Sstevel@tonic-gate dr_check_dip(dev_info_t *dip, void *arg, uint_t ref) 277*0Sstevel@tonic-gate { 278*0Sstevel@tonic-gate major_t major; 279*0Sstevel@tonic-gate char *dname; 280*0Sstevel@tonic-gate struct dr_ref *rp = (struct dr_ref *)arg; 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate if (dip == NULL) 283*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate if (!dr_is_real_device(dip)) 286*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 287*0Sstevel@tonic-gate 288*0Sstevel@tonic-gate dname = ddi_binding_name(dip); 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate if (dr_bypass_device(dname)) 291*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate if (dname && ((major = ddi_name_to_major(dname)) != (major_t)-1)) { 294*0Sstevel@tonic-gate if (ref && rp->refcount) { 295*0Sstevel@tonic-gate *rp->refcount += ref; 296*0Sstevel@tonic-gate PR_QR("\n %s (major# %d) is referenced(%u)\n", 297*0Sstevel@tonic-gate dname, major, ref); 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate if (dr_is_unsafe_major(major) && 300*0Sstevel@tonic-gate i_ddi_node_state(dip) >= DS_ATTACHED) { 301*0Sstevel@tonic-gate PR_QR("\n %s (major# %d) not hotpluggable\n", 302*0Sstevel@tonic-gate dname, major); 303*0Sstevel@tonic-gate if (rp->arr != NULL && rp->idx != NULL) 304*0Sstevel@tonic-gate *rp->idx = dr_add_int(rp->arr, *rp->idx, 305*0Sstevel@tonic-gate rp->len, (uint64_t)major); 306*0Sstevel@tonic-gate } 307*0Sstevel@tonic-gate } 308*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 309*0Sstevel@tonic-gate } 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate static int 312*0Sstevel@tonic-gate dr_check_unsafe_major(dev_info_t *dip, void *arg) 313*0Sstevel@tonic-gate { 314*0Sstevel@tonic-gate return (dr_check_dip(dip, arg, 0)); 315*0Sstevel@tonic-gate } 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate /*ARGSUSED*/ 319*0Sstevel@tonic-gate void 320*0Sstevel@tonic-gate dr_check_devices(dev_info_t *dip, int *refcount, dr_handle_t *handle, 321*0Sstevel@tonic-gate uint64_t *arr, int *idx, int len) 322*0Sstevel@tonic-gate { 323*0Sstevel@tonic-gate struct dr_ref bref = {0}; 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate if (dip == NULL) 326*0Sstevel@tonic-gate return; 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate bref.refcount = refcount; 329*0Sstevel@tonic-gate bref.arr = arr; 330*0Sstevel@tonic-gate bref.idx = idx; 331*0Sstevel@tonic-gate bref.len = len; 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate ASSERT(e_ddi_branch_held(dip)); 334*0Sstevel@tonic-gate (void) e_ddi_branch_referenced(dip, dr_check_dip, &bref); 335*0Sstevel@tonic-gate } 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate /* 338*0Sstevel@tonic-gate * The "dip" argument's parent (if it exists) must be held busy. 339*0Sstevel@tonic-gate */ 340*0Sstevel@tonic-gate static int 341*0Sstevel@tonic-gate dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh) 342*0Sstevel@tonic-gate { 343*0Sstevel@tonic-gate dr_handle_t *handle; 344*0Sstevel@tonic-gate major_t major; 345*0Sstevel@tonic-gate char *dname; 346*0Sstevel@tonic-gate int circ; 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate /* 349*0Sstevel@tonic-gate * If dip is the root node, it has no siblings and it is 350*0Sstevel@tonic-gate * always held. If dip is not the root node, dr_suspend_devices() 351*0Sstevel@tonic-gate * will be invoked with the parent held busy. 352*0Sstevel@tonic-gate */ 353*0Sstevel@tonic-gate for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 354*0Sstevel@tonic-gate char d_name[40], d_alias[40], *d_info; 355*0Sstevel@tonic-gate 356*0Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 357*0Sstevel@tonic-gate if (dr_suspend_devices(ddi_get_child(dip), srh)) { 358*0Sstevel@tonic-gate ndi_devi_exit(dip, circ); 359*0Sstevel@tonic-gate return (ENXIO); 360*0Sstevel@tonic-gate } 361*0Sstevel@tonic-gate ndi_devi_exit(dip, circ); 362*0Sstevel@tonic-gate 363*0Sstevel@tonic-gate if (!dr_is_real_device(dip)) 364*0Sstevel@tonic-gate continue; 365*0Sstevel@tonic-gate 366*0Sstevel@tonic-gate major = (major_t)-1; 367*0Sstevel@tonic-gate if ((dname = ddi_binding_name(dip)) != NULL) 368*0Sstevel@tonic-gate major = ddi_name_to_major(dname); 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate if (dr_bypass_device(dname)) { 371*0Sstevel@tonic-gate PR_QR(" bypassed suspend of %s (major# %d)\n", dname, 372*0Sstevel@tonic-gate major); 373*0Sstevel@tonic-gate continue; 374*0Sstevel@tonic-gate } 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate if (drmach_verify_sr(dip, 1)) { 377*0Sstevel@tonic-gate PR_QR(" bypassed suspend of %s (major# %d)\n", dname, 378*0Sstevel@tonic-gate major); 379*0Sstevel@tonic-gate continue; 380*0Sstevel@tonic-gate } 381*0Sstevel@tonic-gate 382*0Sstevel@tonic-gate if ((d_info = ddi_get_name_addr(dip)) == NULL) 383*0Sstevel@tonic-gate d_info = "<null>"; 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gate d_name[0] = 0; 386*0Sstevel@tonic-gate if (dr_resolve_devname(dip, d_name, d_alias) == 0) { 387*0Sstevel@tonic-gate if (d_alias[0] != 0) { 388*0Sstevel@tonic-gate prom_printf("\tsuspending %s@%s (aka %s)\n", 389*0Sstevel@tonic-gate d_name, d_info, d_alias); 390*0Sstevel@tonic-gate } else { 391*0Sstevel@tonic-gate prom_printf("\tsuspending %s@%s\n", 392*0Sstevel@tonic-gate d_name, d_info); 393*0Sstevel@tonic-gate } 394*0Sstevel@tonic-gate } else { 395*0Sstevel@tonic-gate prom_printf("\tsuspending %s@%s\n", dname, d_info); 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate 398*0Sstevel@tonic-gate if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) { 399*0Sstevel@tonic-gate prom_printf("\tFAILED to suspend %s@%s\n", 400*0Sstevel@tonic-gate d_name[0] ? d_name : dname, d_info); 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate srh->sr_err_idx = dr_add_int(srh->sr_err_ints, 403*0Sstevel@tonic-gate srh->sr_err_idx, DR_MAX_ERR_INT, 404*0Sstevel@tonic-gate (uint64_t)major); 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate ndi_hold_devi(dip); 407*0Sstevel@tonic-gate srh->sr_failed_dip = dip; 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate handle = srh->sr_dr_handlep; 410*0Sstevel@tonic-gate dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s", 411*0Sstevel@tonic-gate d_name[0] ? d_name : dname, d_info); 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate return (DDI_FAILURE); 414*0Sstevel@tonic-gate } 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate return (DDI_SUCCESS); 418*0Sstevel@tonic-gate } 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate static void 421*0Sstevel@tonic-gate dr_resume_devices(dev_info_t *start, dr_sr_handle_t *srh) 422*0Sstevel@tonic-gate { 423*0Sstevel@tonic-gate dr_handle_t *handle; 424*0Sstevel@tonic-gate dev_info_t *dip, *next, *last = NULL; 425*0Sstevel@tonic-gate major_t major; 426*0Sstevel@tonic-gate char *bn; 427*0Sstevel@tonic-gate int circ; 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate major = (major_t)-1; 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate /* attach in reverse device tree order */ 432*0Sstevel@tonic-gate while (last != start) { 433*0Sstevel@tonic-gate dip = start; 434*0Sstevel@tonic-gate next = ddi_get_next_sibling(dip); 435*0Sstevel@tonic-gate while (next != last && dip != srh->sr_failed_dip) { 436*0Sstevel@tonic-gate dip = next; 437*0Sstevel@tonic-gate next = ddi_get_next_sibling(dip); 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate if (dip == srh->sr_failed_dip) { 440*0Sstevel@tonic-gate /* release hold acquired in dr_suspend_devices() */ 441*0Sstevel@tonic-gate srh->sr_failed_dip = NULL; 442*0Sstevel@tonic-gate ndi_rele_devi(dip); 443*0Sstevel@tonic-gate } else if (dr_is_real_device(dip) && 444*0Sstevel@tonic-gate srh->sr_failed_dip == NULL) { 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate if ((bn = ddi_binding_name(dip)) != NULL) { 447*0Sstevel@tonic-gate major = ddi_name_to_major(bn); 448*0Sstevel@tonic-gate } else { 449*0Sstevel@tonic-gate bn = "<null>"; 450*0Sstevel@tonic-gate } 451*0Sstevel@tonic-gate if (!dr_bypass_device(bn) && 452*0Sstevel@tonic-gate !drmach_verify_sr(dip, 0)) { 453*0Sstevel@tonic-gate char d_name[40], d_alias[40], *d_info; 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate d_name[0] = 0; 456*0Sstevel@tonic-gate d_info = ddi_get_name_addr(dip); 457*0Sstevel@tonic-gate if (d_info == NULL) 458*0Sstevel@tonic-gate d_info = "<null>"; 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate if (!dr_resolve_devname(dip, d_name, 461*0Sstevel@tonic-gate d_alias)) { 462*0Sstevel@tonic-gate if (d_alias[0] != 0) { 463*0Sstevel@tonic-gate prom_printf("\tresuming " 464*0Sstevel@tonic-gate "%s@%s (aka %s)\n", 465*0Sstevel@tonic-gate d_name, d_info, 466*0Sstevel@tonic-gate d_alias); 467*0Sstevel@tonic-gate } else { 468*0Sstevel@tonic-gate prom_printf("\tresuming " 469*0Sstevel@tonic-gate "%s@%s\n", 470*0Sstevel@tonic-gate d_name, d_info); 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate } else { 473*0Sstevel@tonic-gate prom_printf("\tresuming %s@%s\n", 474*0Sstevel@tonic-gate bn, d_info); 475*0Sstevel@tonic-gate } 476*0Sstevel@tonic-gate 477*0Sstevel@tonic-gate if (devi_attach(dip, DDI_RESUME) != 478*0Sstevel@tonic-gate DDI_SUCCESS) { 479*0Sstevel@tonic-gate /* 480*0Sstevel@tonic-gate * Print a console warning, 481*0Sstevel@tonic-gate * set an e_code of ESBD_RESUME, 482*0Sstevel@tonic-gate * and save the driver major 483*0Sstevel@tonic-gate * number in the e_rsc. 484*0Sstevel@tonic-gate */ 485*0Sstevel@tonic-gate prom_printf("\tFAILED to resume %s@%s", 486*0Sstevel@tonic-gate d_name[0] ? d_name : bn, d_info); 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate srh->sr_err_idx = 489*0Sstevel@tonic-gate dr_add_int(srh->sr_err_ints, 490*0Sstevel@tonic-gate srh->sr_err_idx, DR_MAX_ERR_INT, 491*0Sstevel@tonic-gate (uint64_t)major); 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate handle = srh->sr_dr_handlep; 494*0Sstevel@tonic-gate 495*0Sstevel@tonic-gate dr_op_err(CE_IGNORE, handle, 496*0Sstevel@tonic-gate ESBD_RESUME, "%s@%s", 497*0Sstevel@tonic-gate d_name[0] ? d_name : bn, d_info); 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate } 500*0Sstevel@tonic-gate } 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gate /* Hold parent busy while walking its children */ 503*0Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 504*0Sstevel@tonic-gate dr_resume_devices(ddi_get_child(dip), srh); 505*0Sstevel@tonic-gate ndi_devi_exit(dip, circ); 506*0Sstevel@tonic-gate last = dip; 507*0Sstevel@tonic-gate } 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate /* 511*0Sstevel@tonic-gate * True if thread is virtually stopped. Similar to CPR_VSTOPPED 512*0Sstevel@tonic-gate * but from DR point of view. These user threads are waiting in 513*0Sstevel@tonic-gate * the kernel. Once they complete in the kernel, they will process 514*0Sstevel@tonic-gate * the stop signal and stop. 515*0Sstevel@tonic-gate */ 516*0Sstevel@tonic-gate #define DR_VSTOPPED(t) \ 517*0Sstevel@tonic-gate ((t)->t_state == TS_SLEEP && \ 518*0Sstevel@tonic-gate (t)->t_wchan != NULL && \ 519*0Sstevel@tonic-gate (t)->t_astflag && \ 520*0Sstevel@tonic-gate ((t)->t_proc_flag & TP_CHKPT)) 521*0Sstevel@tonic-gate 522*0Sstevel@tonic-gate /* ARGSUSED */ 523*0Sstevel@tonic-gate static int 524*0Sstevel@tonic-gate dr_stop_user_threads(dr_sr_handle_t *srh) 525*0Sstevel@tonic-gate { 526*0Sstevel@tonic-gate int count; 527*0Sstevel@tonic-gate int bailout; 528*0Sstevel@tonic-gate dr_handle_t *handle = srh->sr_dr_handlep; 529*0Sstevel@tonic-gate static fn_t f = "dr_stop_user_threads"; 530*0Sstevel@tonic-gate kthread_id_t tp; 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate extern void add_one_utstop(); 533*0Sstevel@tonic-gate extern void utstop_timedwait(clock_t); 534*0Sstevel@tonic-gate extern void utstop_init(void); 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate #define DR_UTSTOP_RETRY 4 537*0Sstevel@tonic-gate #define DR_UTSTOP_WAIT hz 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate if (dr_skip_user_threads) 540*0Sstevel@tonic-gate return (DDI_SUCCESS); 541*0Sstevel@tonic-gate 542*0Sstevel@tonic-gate utstop_init(); 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate /* we need to try a few times to get past fork, etc. */ 545*0Sstevel@tonic-gate srh->sr_err_idx = 0; 546*0Sstevel@tonic-gate for (count = 0; count < DR_UTSTOP_RETRY; count++) { 547*0Sstevel@tonic-gate /* walk the entire threadlist */ 548*0Sstevel@tonic-gate mutex_enter(&pidlock); 549*0Sstevel@tonic-gate for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) { 550*0Sstevel@tonic-gate proc_t *p = ttoproc(tp); 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate /* handle kernel threads separately */ 553*0Sstevel@tonic-gate if (p->p_as == &kas || p->p_stat == SZOMB) 554*0Sstevel@tonic-gate continue; 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate mutex_enter(&p->p_lock); 557*0Sstevel@tonic-gate thread_lock(tp); 558*0Sstevel@tonic-gate 559*0Sstevel@tonic-gate if (tp->t_state == TS_STOPPED) { 560*0Sstevel@tonic-gate /* add another reason to stop this thread */ 561*0Sstevel@tonic-gate tp->t_schedflag &= ~TS_RESUME; 562*0Sstevel@tonic-gate } else { 563*0Sstevel@tonic-gate tp->t_proc_flag |= TP_CHKPT; 564*0Sstevel@tonic-gate 565*0Sstevel@tonic-gate thread_unlock(tp); 566*0Sstevel@tonic-gate mutex_exit(&p->p_lock); 567*0Sstevel@tonic-gate add_one_utstop(); 568*0Sstevel@tonic-gate mutex_enter(&p->p_lock); 569*0Sstevel@tonic-gate thread_lock(tp); 570*0Sstevel@tonic-gate 571*0Sstevel@tonic-gate aston(tp); 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate if (tp->t_state == TS_SLEEP && 574*0Sstevel@tonic-gate (tp->t_flag & T_WAKEABLE)) { 575*0Sstevel@tonic-gate setrun_locked(tp); 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate } 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate /* grab thread if needed */ 581*0Sstevel@tonic-gate if (tp->t_state == TS_ONPROC && tp->t_cpu != CPU) 582*0Sstevel@tonic-gate poke_cpu(tp->t_cpu->cpu_id); 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate thread_unlock(tp); 586*0Sstevel@tonic-gate mutex_exit(&p->p_lock); 587*0Sstevel@tonic-gate } 588*0Sstevel@tonic-gate mutex_exit(&pidlock); 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate /* let everything catch up */ 592*0Sstevel@tonic-gate utstop_timedwait(count * count * DR_UTSTOP_WAIT); 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gate 595*0Sstevel@tonic-gate /* now, walk the threadlist again to see if we are done */ 596*0Sstevel@tonic-gate mutex_enter(&pidlock); 597*0Sstevel@tonic-gate for (tp = curthread->t_next, bailout = 0; 598*0Sstevel@tonic-gate tp != curthread; tp = tp->t_next) { 599*0Sstevel@tonic-gate proc_t *p = ttoproc(tp); 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate /* handle kernel threads separately */ 602*0Sstevel@tonic-gate if (p->p_as == &kas || p->p_stat == SZOMB) 603*0Sstevel@tonic-gate continue; 604*0Sstevel@tonic-gate 605*0Sstevel@tonic-gate /* 606*0Sstevel@tonic-gate * If this thread didn't stop, and we don't allow 607*0Sstevel@tonic-gate * unstopped blocked threads, bail. 608*0Sstevel@tonic-gate */ 609*0Sstevel@tonic-gate thread_lock(tp); 610*0Sstevel@tonic-gate if (!CPR_ISTOPPED(tp) && 611*0Sstevel@tonic-gate !(dr_allow_blocked_threads && 612*0Sstevel@tonic-gate DR_VSTOPPED(tp))) { 613*0Sstevel@tonic-gate bailout = 1; 614*0Sstevel@tonic-gate if (count == DR_UTSTOP_RETRY - 1) { 615*0Sstevel@tonic-gate /* 616*0Sstevel@tonic-gate * save the pid for later reporting 617*0Sstevel@tonic-gate */ 618*0Sstevel@tonic-gate srh->sr_err_idx = 619*0Sstevel@tonic-gate dr_add_int(srh->sr_err_ints, 620*0Sstevel@tonic-gate srh->sr_err_idx, DR_MAX_ERR_INT, 621*0Sstevel@tonic-gate (uint64_t)p->p_pid); 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s: " 624*0Sstevel@tonic-gate "failed to stop thread: " 625*0Sstevel@tonic-gate "process=%s, pid=%d", 626*0Sstevel@tonic-gate f, p->p_user.u_psargs, p->p_pid); 627*0Sstevel@tonic-gate 628*0Sstevel@tonic-gate PR_QR("%s: failed to stop thread: " 629*0Sstevel@tonic-gate "process=%s, pid=%d, t_id=0x%lx, " 630*0Sstevel@tonic-gate "t_state=0x%x, t_proc_flag=0x%x, " 631*0Sstevel@tonic-gate "t_schedflag=0x%x\n", 632*0Sstevel@tonic-gate f, p->p_user.u_psargs, p->p_pid, 633*0Sstevel@tonic-gate tp, tp->t_state, tp->t_proc_flag, 634*0Sstevel@tonic-gate tp->t_schedflag); 635*0Sstevel@tonic-gate } 636*0Sstevel@tonic-gate 637*0Sstevel@tonic-gate } 638*0Sstevel@tonic-gate thread_unlock(tp); 639*0Sstevel@tonic-gate } 640*0Sstevel@tonic-gate mutex_exit(&pidlock); 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate /* were all the threads stopped? */ 643*0Sstevel@tonic-gate if (!bailout) 644*0Sstevel@tonic-gate break; 645*0Sstevel@tonic-gate } 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate /* were we unable to stop all threads after a few tries? */ 648*0Sstevel@tonic-gate if (bailout) { 649*0Sstevel@tonic-gate handle->h_err = drerr_int(ESBD_UTHREAD, srh->sr_err_ints, 650*0Sstevel@tonic-gate srh->sr_err_idx, 0); 651*0Sstevel@tonic-gate return (ESRCH); 652*0Sstevel@tonic-gate } 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate return (DDI_SUCCESS); 655*0Sstevel@tonic-gate } 656*0Sstevel@tonic-gate 657*0Sstevel@tonic-gate static int 658*0Sstevel@tonic-gate dr_stop_kernel_threads(dr_handle_t *handle) 659*0Sstevel@tonic-gate { 660*0Sstevel@tonic-gate caddr_t name; 661*0Sstevel@tonic-gate kthread_id_t tp; 662*0Sstevel@tonic-gate 663*0Sstevel@tonic-gate if (dr_skip_kernel_threads) { 664*0Sstevel@tonic-gate return (DDI_SUCCESS); 665*0Sstevel@tonic-gate } 666*0Sstevel@tonic-gate 667*0Sstevel@tonic-gate /* 668*0Sstevel@tonic-gate * Note: we unlock the table in resume. 669*0Sstevel@tonic-gate * We need to lock the callback table only if we are actually 670*0Sstevel@tonic-gate * suspending kernel threads. 671*0Sstevel@tonic-gate */ 672*0Sstevel@tonic-gate callb_lock_table(); 673*0Sstevel@tonic-gate name = callb_execute_class(CB_CL_CPR_DAEMON, CB_CODE_CPR_CHKPT); 674*0Sstevel@tonic-gate if (name != NULL) { 675*0Sstevel@tonic-gate dr_op_err(CE_IGNORE, handle, ESBD_KTHREAD, name); 676*0Sstevel@tonic-gate return (EBUSY); 677*0Sstevel@tonic-gate } 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate /* 680*0Sstevel@tonic-gate * Verify that all threads are accounted for 681*0Sstevel@tonic-gate */ 682*0Sstevel@tonic-gate mutex_enter(&pidlock); 683*0Sstevel@tonic-gate for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) { 684*0Sstevel@tonic-gate proc_t *p = ttoproc(tp); 685*0Sstevel@tonic-gate 686*0Sstevel@tonic-gate if (p->p_as != &kas) 687*0Sstevel@tonic-gate continue; 688*0Sstevel@tonic-gate 689*0Sstevel@tonic-gate if (tp->t_flag & T_INTR_THREAD) 690*0Sstevel@tonic-gate continue; 691*0Sstevel@tonic-gate 692*0Sstevel@tonic-gate if (!callb_is_stopped(tp, &name)) { 693*0Sstevel@tonic-gate mutex_exit(&pidlock); 694*0Sstevel@tonic-gate dr_op_err(CE_IGNORE, handle, ESBD_KTHREAD, name); 695*0Sstevel@tonic-gate return (EBUSY); 696*0Sstevel@tonic-gate } 697*0Sstevel@tonic-gate } 698*0Sstevel@tonic-gate 699*0Sstevel@tonic-gate mutex_exit(&pidlock); 700*0Sstevel@tonic-gate return (DDI_SUCCESS); 701*0Sstevel@tonic-gate } 702*0Sstevel@tonic-gate 703*0Sstevel@tonic-gate static void 704*0Sstevel@tonic-gate dr_start_user_threads(void) 705*0Sstevel@tonic-gate { 706*0Sstevel@tonic-gate kthread_id_t tp; 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate mutex_enter(&pidlock); 709*0Sstevel@tonic-gate 710*0Sstevel@tonic-gate /* walk all threads and release them */ 711*0Sstevel@tonic-gate for (tp = curthread->t_next; tp != curthread; tp = tp->t_next) { 712*0Sstevel@tonic-gate proc_t *p = ttoproc(tp); 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate /* skip kernel threads */ 715*0Sstevel@tonic-gate if (ttoproc(tp)->p_as == &kas) 716*0Sstevel@tonic-gate continue; 717*0Sstevel@tonic-gate 718*0Sstevel@tonic-gate mutex_enter(&p->p_lock); 719*0Sstevel@tonic-gate tp->t_proc_flag &= ~TP_CHKPT; 720*0Sstevel@tonic-gate mutex_exit(&p->p_lock); 721*0Sstevel@tonic-gate 722*0Sstevel@tonic-gate thread_lock(tp); 723*0Sstevel@tonic-gate if (CPR_ISTOPPED(tp)) { 724*0Sstevel@tonic-gate /* back on the runq */ 725*0Sstevel@tonic-gate tp->t_schedflag |= TS_RESUME; 726*0Sstevel@tonic-gate setrun_locked(tp); 727*0Sstevel@tonic-gate } 728*0Sstevel@tonic-gate thread_unlock(tp); 729*0Sstevel@tonic-gate } 730*0Sstevel@tonic-gate 731*0Sstevel@tonic-gate mutex_exit(&pidlock); 732*0Sstevel@tonic-gate } 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gate static void 735*0Sstevel@tonic-gate dr_signal_user(int sig) 736*0Sstevel@tonic-gate { 737*0Sstevel@tonic-gate struct proc *p; 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate mutex_enter(&pidlock); 740*0Sstevel@tonic-gate 741*0Sstevel@tonic-gate for (p = practive; p != NULL; p = p->p_next) { 742*0Sstevel@tonic-gate /* only user threads */ 743*0Sstevel@tonic-gate if (p->p_exec == NULL || p->p_stat == SZOMB || 744*0Sstevel@tonic-gate p == proc_init || p == ttoproc(curthread)) 745*0Sstevel@tonic-gate continue; 746*0Sstevel@tonic-gate 747*0Sstevel@tonic-gate mutex_enter(&p->p_lock); 748*0Sstevel@tonic-gate sigtoproc(p, NULL, sig); 749*0Sstevel@tonic-gate mutex_exit(&p->p_lock); 750*0Sstevel@tonic-gate } 751*0Sstevel@tonic-gate 752*0Sstevel@tonic-gate mutex_exit(&pidlock); 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate /* add a bit of delay */ 755*0Sstevel@tonic-gate delay(hz); 756*0Sstevel@tonic-gate } 757*0Sstevel@tonic-gate 758*0Sstevel@tonic-gate void 759*0Sstevel@tonic-gate dr_resume(dr_sr_handle_t *srh) 760*0Sstevel@tonic-gate { 761*0Sstevel@tonic-gate dr_handle_t *handle; 762*0Sstevel@tonic-gate 763*0Sstevel@tonic-gate handle = srh->sr_dr_handlep; 764*0Sstevel@tonic-gate 765*0Sstevel@tonic-gate if (srh->sr_suspend_state < DR_SRSTATE_FULL) { 766*0Sstevel@tonic-gate /* 767*0Sstevel@tonic-gate * Update the signature block. 768*0Sstevel@tonic-gate * If cpus are not paused, this can be done now. 769*0Sstevel@tonic-gate * See comments below. 770*0Sstevel@tonic-gate */ 771*0Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL, 772*0Sstevel@tonic-gate CPU->cpu_id); 773*0Sstevel@tonic-gate } 774*0Sstevel@tonic-gate 775*0Sstevel@tonic-gate switch (srh->sr_suspend_state) { 776*0Sstevel@tonic-gate case DR_SRSTATE_FULL: 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 779*0Sstevel@tonic-gate 780*0Sstevel@tonic-gate dr_enable_intr(); /* enable intr & clock */ 781*0Sstevel@tonic-gate 782*0Sstevel@tonic-gate start_cpus(); 783*0Sstevel@tonic-gate mutex_exit(&cpu_lock); 784*0Sstevel@tonic-gate 785*0Sstevel@tonic-gate /* 786*0Sstevel@tonic-gate * Update the signature block. 787*0Sstevel@tonic-gate * This must not be done while cpus are paused, since on 788*0Sstevel@tonic-gate * Starcat the cpu signature update aquires an adaptive 789*0Sstevel@tonic-gate * mutex in the iosram driver. Blocking with cpus paused 790*0Sstevel@tonic-gate * can lead to deadlock. 791*0Sstevel@tonic-gate */ 792*0Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_RESUME_INPROGRESS, SIGSUBST_NULL, 793*0Sstevel@tonic-gate CPU->cpu_id); 794*0Sstevel@tonic-gate 795*0Sstevel@tonic-gate /* 796*0Sstevel@tonic-gate * If we suspended hw watchdog at suspend, 797*0Sstevel@tonic-gate * re-enable it now. 798*0Sstevel@tonic-gate */ 799*0Sstevel@tonic-gate 800*0Sstevel@tonic-gate if (srh->sr_flags & (SR_FLAG_WATCHDOG)) { 801*0Sstevel@tonic-gate mutex_enter(&tod_lock); 802*0Sstevel@tonic-gate tod_ops.tod_set_watchdog_timer( 803*0Sstevel@tonic-gate watchdog_timeout_seconds); 804*0Sstevel@tonic-gate mutex_exit(&tod_lock); 805*0Sstevel@tonic-gate } 806*0Sstevel@tonic-gate 807*0Sstevel@tonic-gate /* 808*0Sstevel@tonic-gate * This should only be called if drmach_suspend_last() 809*0Sstevel@tonic-gate * was called and state transitioned to DR_SRSTATE_FULL 810*0Sstevel@tonic-gate * to prevent resume attempts on device instances that 811*0Sstevel@tonic-gate * were not previously suspended. 812*0Sstevel@tonic-gate */ 813*0Sstevel@tonic-gate drmach_resume_first(); 814*0Sstevel@tonic-gate 815*0Sstevel@tonic-gate /* FALLTHROUGH */ 816*0Sstevel@tonic-gate 817*0Sstevel@tonic-gate case DR_SRSTATE_DRIVER: 818*0Sstevel@tonic-gate /* 819*0Sstevel@tonic-gate * resume drivers 820*0Sstevel@tonic-gate */ 821*0Sstevel@tonic-gate srh->sr_err_idx = 0; 822*0Sstevel@tonic-gate 823*0Sstevel@tonic-gate /* no parent dip to hold busy */ 824*0Sstevel@tonic-gate dr_resume_devices(ddi_root_node(), srh); 825*0Sstevel@tonic-gate 826*0Sstevel@tonic-gate if (srh->sr_err_idx && srh->sr_dr_handlep) { 827*0Sstevel@tonic-gate (srh->sr_dr_handlep)->h_err = drerr_int(ESBD_RESUME, 828*0Sstevel@tonic-gate srh->sr_err_ints, srh->sr_err_idx, 1); 829*0Sstevel@tonic-gate } 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate /* 832*0Sstevel@tonic-gate * resume the lock manager 833*0Sstevel@tonic-gate */ 834*0Sstevel@tonic-gate lm_cprresume(); 835*0Sstevel@tonic-gate 836*0Sstevel@tonic-gate /* FALLTHROUGH */ 837*0Sstevel@tonic-gate 838*0Sstevel@tonic-gate case DR_SRSTATE_DAEMON: 839*0Sstevel@tonic-gate /* 840*0Sstevel@tonic-gate * resume kernel daemons 841*0Sstevel@tonic-gate */ 842*0Sstevel@tonic-gate if (!dr_skip_kernel_threads) { 843*0Sstevel@tonic-gate prom_printf("DR: resuming kernel daemons...\n"); 844*0Sstevel@tonic-gate (void) callb_execute_class(CB_CL_CPR_DAEMON, 845*0Sstevel@tonic-gate CB_CODE_CPR_RESUME); 846*0Sstevel@tonic-gate callb_unlock_table(); 847*0Sstevel@tonic-gate } 848*0Sstevel@tonic-gate 849*0Sstevel@tonic-gate /* FALLTHROUGH */ 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate case DR_SRSTATE_USER: 852*0Sstevel@tonic-gate /* 853*0Sstevel@tonic-gate * finally, resume user threads 854*0Sstevel@tonic-gate */ 855*0Sstevel@tonic-gate if (!dr_skip_user_threads) { 856*0Sstevel@tonic-gate prom_printf("DR: resuming user threads...\n"); 857*0Sstevel@tonic-gate dr_start_user_threads(); 858*0Sstevel@tonic-gate } 859*0Sstevel@tonic-gate /* FALLTHROUGH */ 860*0Sstevel@tonic-gate 861*0Sstevel@tonic-gate case DR_SRSTATE_BEGIN: 862*0Sstevel@tonic-gate default: 863*0Sstevel@tonic-gate /* 864*0Sstevel@tonic-gate * let those who care know that we've just resumed 865*0Sstevel@tonic-gate */ 866*0Sstevel@tonic-gate PR_QR("sending SIGTHAW...\n"); 867*0Sstevel@tonic-gate dr_signal_user(SIGTHAW); 868*0Sstevel@tonic-gate break; 869*0Sstevel@tonic-gate } 870*0Sstevel@tonic-gate 871*0Sstevel@tonic-gate i_ndi_allow_device_tree_changes(handle->h_ndi); 872*0Sstevel@tonic-gate 873*0Sstevel@tonic-gate /* 874*0Sstevel@tonic-gate * update the signature block 875*0Sstevel@tonic-gate */ 876*0Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, CPU->cpu_id); 877*0Sstevel@tonic-gate 878*0Sstevel@tonic-gate prom_printf("DR: resume COMPLETED\n"); 879*0Sstevel@tonic-gate } 880*0Sstevel@tonic-gate 881*0Sstevel@tonic-gate int 882*0Sstevel@tonic-gate dr_suspend(dr_sr_handle_t *srh) 883*0Sstevel@tonic-gate { 884*0Sstevel@tonic-gate dr_handle_t *handle; 885*0Sstevel@tonic-gate int force; 886*0Sstevel@tonic-gate int dev_errs_idx; 887*0Sstevel@tonic-gate uint64_t dev_errs[DR_MAX_ERR_INT]; 888*0Sstevel@tonic-gate int rc = DDI_SUCCESS; 889*0Sstevel@tonic-gate 890*0Sstevel@tonic-gate handle = srh->sr_dr_handlep; 891*0Sstevel@tonic-gate 892*0Sstevel@tonic-gate force = dr_cmd_flags(handle) & SBD_FLAG_FORCE; 893*0Sstevel@tonic-gate 894*0Sstevel@tonic-gate /* 895*0Sstevel@tonic-gate * update the signature block 896*0Sstevel@tonic-gate */ 897*0Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_QUIESCE_INPROGRESS, SIGSUBST_NULL, 898*0Sstevel@tonic-gate CPU->cpu_id); 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate i_ndi_block_device_tree_changes(&handle->h_ndi); 901*0Sstevel@tonic-gate 902*0Sstevel@tonic-gate prom_printf("\nDR: suspending user threads...\n"); 903*0Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_USER; 904*0Sstevel@tonic-gate if (((rc = dr_stop_user_threads(srh)) != DDI_SUCCESS) && 905*0Sstevel@tonic-gate dr_check_user_stop_result) { 906*0Sstevel@tonic-gate dr_resume(srh); 907*0Sstevel@tonic-gate return (rc); 908*0Sstevel@tonic-gate } 909*0Sstevel@tonic-gate 910*0Sstevel@tonic-gate if (!force) { 911*0Sstevel@tonic-gate struct dr_ref drc = {0}; 912*0Sstevel@tonic-gate 913*0Sstevel@tonic-gate prom_printf("\nDR: checking devices...\n"); 914*0Sstevel@tonic-gate dev_errs_idx = 0; 915*0Sstevel@tonic-gate 916*0Sstevel@tonic-gate drc.arr = dev_errs; 917*0Sstevel@tonic-gate drc.idx = &dev_errs_idx; 918*0Sstevel@tonic-gate drc.len = DR_MAX_ERR_INT; 919*0Sstevel@tonic-gate 920*0Sstevel@tonic-gate /* 921*0Sstevel@tonic-gate * Since the root node can never go away, it 922*0Sstevel@tonic-gate * doesn't have to be held. 923*0Sstevel@tonic-gate */ 924*0Sstevel@tonic-gate ddi_walk_devs(ddi_root_node(), dr_check_unsafe_major, &drc); 925*0Sstevel@tonic-gate if (dev_errs_idx) { 926*0Sstevel@tonic-gate handle->h_err = drerr_int(ESBD_UNSAFE, dev_errs, 927*0Sstevel@tonic-gate dev_errs_idx, 1); 928*0Sstevel@tonic-gate dr_resume(srh); 929*0Sstevel@tonic-gate return (DDI_FAILURE); 930*0Sstevel@tonic-gate } 931*0Sstevel@tonic-gate PR_QR("done\n"); 932*0Sstevel@tonic-gate } else { 933*0Sstevel@tonic-gate prom_printf("\nDR: dr_suspend invoked with force flag\n"); 934*0Sstevel@tonic-gate } 935*0Sstevel@tonic-gate 936*0Sstevel@tonic-gate /* 937*0Sstevel@tonic-gate * now stop daemon activities 938*0Sstevel@tonic-gate */ 939*0Sstevel@tonic-gate prom_printf("DR: suspending kernel daemons...\n"); 940*0Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_DAEMON; 941*0Sstevel@tonic-gate if ((rc = dr_stop_kernel_threads(handle)) != DDI_SUCCESS) { 942*0Sstevel@tonic-gate dr_resume(srh); 943*0Sstevel@tonic-gate return (rc); 944*0Sstevel@tonic-gate } 945*0Sstevel@tonic-gate 946*0Sstevel@tonic-gate #ifndef SKIP_SYNC 947*0Sstevel@tonic-gate /* 948*0Sstevel@tonic-gate * This sync swap out all user pages 949*0Sstevel@tonic-gate */ 950*0Sstevel@tonic-gate vfs_sync(SYNC_ALL); 951*0Sstevel@tonic-gate #endif 952*0Sstevel@tonic-gate 953*0Sstevel@tonic-gate /* 954*0Sstevel@tonic-gate * special treatment for lock manager 955*0Sstevel@tonic-gate */ 956*0Sstevel@tonic-gate lm_cprsuspend(); 957*0Sstevel@tonic-gate 958*0Sstevel@tonic-gate #ifndef SKIP_SYNC 959*0Sstevel@tonic-gate /* 960*0Sstevel@tonic-gate * sync the file system in case we never make it back 961*0Sstevel@tonic-gate */ 962*0Sstevel@tonic-gate sync(); 963*0Sstevel@tonic-gate #endif 964*0Sstevel@tonic-gate 965*0Sstevel@tonic-gate /* 966*0Sstevel@tonic-gate * now suspend drivers 967*0Sstevel@tonic-gate */ 968*0Sstevel@tonic-gate prom_printf("DR: suspending drivers...\n"); 969*0Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_DRIVER; 970*0Sstevel@tonic-gate srh->sr_err_idx = 0; 971*0Sstevel@tonic-gate /* No parent to hold busy */ 972*0Sstevel@tonic-gate if ((rc = dr_suspend_devices(ddi_root_node(), srh)) != DDI_SUCCESS) { 973*0Sstevel@tonic-gate if (srh->sr_err_idx && srh->sr_dr_handlep) { 974*0Sstevel@tonic-gate (srh->sr_dr_handlep)->h_err = drerr_int(ESBD_SUSPEND, 975*0Sstevel@tonic-gate srh->sr_err_ints, srh->sr_err_idx, 1); 976*0Sstevel@tonic-gate } 977*0Sstevel@tonic-gate dr_resume(srh); 978*0Sstevel@tonic-gate return (rc); 979*0Sstevel@tonic-gate } 980*0Sstevel@tonic-gate 981*0Sstevel@tonic-gate drmach_suspend_last(); 982*0Sstevel@tonic-gate 983*0Sstevel@tonic-gate /* 984*0Sstevel@tonic-gate * finally, grab all cpus 985*0Sstevel@tonic-gate */ 986*0Sstevel@tonic-gate srh->sr_suspend_state = DR_SRSTATE_FULL; 987*0Sstevel@tonic-gate 988*0Sstevel@tonic-gate /* 989*0Sstevel@tonic-gate * if watchdog was activated, disable it 990*0Sstevel@tonic-gate */ 991*0Sstevel@tonic-gate if (watchdog_activated) { 992*0Sstevel@tonic-gate mutex_enter(&tod_lock); 993*0Sstevel@tonic-gate tod_ops.tod_clear_watchdog_timer(); 994*0Sstevel@tonic-gate mutex_exit(&tod_lock); 995*0Sstevel@tonic-gate srh->sr_flags |= SR_FLAG_WATCHDOG; 996*0Sstevel@tonic-gate } else { 997*0Sstevel@tonic-gate srh->sr_flags &= ~(SR_FLAG_WATCHDOG); 998*0Sstevel@tonic-gate } 999*0Sstevel@tonic-gate 1000*0Sstevel@tonic-gate /* 1001*0Sstevel@tonic-gate * Update the signature block. 1002*0Sstevel@tonic-gate * This must be done before cpus are paused, since on Starcat the 1003*0Sstevel@tonic-gate * cpu signature update aquires an adaptive mutex in the iosram driver. 1004*0Sstevel@tonic-gate * Blocking with cpus paused can lead to deadlock. 1005*0Sstevel@tonic-gate */ 1006*0Sstevel@tonic-gate CPU_SIGNATURE(OS_SIG, SIGST_QUIESCED, SIGSUBST_NULL, CPU->cpu_id); 1007*0Sstevel@tonic-gate 1008*0Sstevel@tonic-gate mutex_enter(&cpu_lock); 1009*0Sstevel@tonic-gate pause_cpus(NULL); 1010*0Sstevel@tonic-gate dr_stop_intr(); 1011*0Sstevel@tonic-gate 1012*0Sstevel@tonic-gate return (rc); 1013*0Sstevel@tonic-gate } 1014*0Sstevel@tonic-gate 1015*0Sstevel@tonic-gate int 1016*0Sstevel@tonic-gate dr_pt_test_suspend(dr_handle_t *hp) 1017*0Sstevel@tonic-gate { 1018*0Sstevel@tonic-gate dr_sr_handle_t *srh; 1019*0Sstevel@tonic-gate int err; 1020*0Sstevel@tonic-gate uint_t psmerr; 1021*0Sstevel@tonic-gate static fn_t f = "dr_pt_test_suspend"; 1022*0Sstevel@tonic-gate 1023*0Sstevel@tonic-gate PR_QR("%s...\n", f); 1024*0Sstevel@tonic-gate 1025*0Sstevel@tonic-gate srh = dr_get_sr_handle(hp); 1026*0Sstevel@tonic-gate if ((err = dr_suspend(srh)) == DDI_SUCCESS) { 1027*0Sstevel@tonic-gate dr_resume(srh); 1028*0Sstevel@tonic-gate if ((hp->h_err) && ((psmerr = hp->h_err->e_code) != 0)) { 1029*0Sstevel@tonic-gate PR_QR("%s: error on dr_resume()", f); 1030*0Sstevel@tonic-gate switch (psmerr) { 1031*0Sstevel@tonic-gate case ESBD_RESUME: 1032*0Sstevel@tonic-gate PR_QR("Couldn't resume devices: %s\n", 1033*0Sstevel@tonic-gate DR_GET_E_RSC(hp->h_err)); 1034*0Sstevel@tonic-gate break; 1035*0Sstevel@tonic-gate 1036*0Sstevel@tonic-gate case ESBD_KTHREAD: 1037*0Sstevel@tonic-gate PR_ALL("psmerr is ESBD_KTHREAD\n"); 1038*0Sstevel@tonic-gate break; 1039*0Sstevel@tonic-gate default: 1040*0Sstevel@tonic-gate PR_ALL("Resume error unknown = %d\n", 1041*0Sstevel@tonic-gate psmerr); 1042*0Sstevel@tonic-gate break; 1043*0Sstevel@tonic-gate } 1044*0Sstevel@tonic-gate } 1045*0Sstevel@tonic-gate } else { 1046*0Sstevel@tonic-gate PR_ALL("%s: dr_suspend() failed, err = 0x%x\n", 1047*0Sstevel@tonic-gate f, err); 1048*0Sstevel@tonic-gate psmerr = hp->h_err ? hp->h_err->e_code : ESBD_NOERROR; 1049*0Sstevel@tonic-gate switch (psmerr) { 1050*0Sstevel@tonic-gate case ESBD_UNSAFE: 1051*0Sstevel@tonic-gate PR_ALL("Unsafe devices (major #): %s\n", 1052*0Sstevel@tonic-gate DR_GET_E_RSC(hp->h_err)); 1053*0Sstevel@tonic-gate break; 1054*0Sstevel@tonic-gate 1055*0Sstevel@tonic-gate case ESBD_RTTHREAD: 1056*0Sstevel@tonic-gate PR_ALL("RT threads (PIDs): %s\n", 1057*0Sstevel@tonic-gate DR_GET_E_RSC(hp->h_err)); 1058*0Sstevel@tonic-gate break; 1059*0Sstevel@tonic-gate 1060*0Sstevel@tonic-gate case ESBD_UTHREAD: 1061*0Sstevel@tonic-gate PR_ALL("User threads (PIDs): %s\n", 1062*0Sstevel@tonic-gate DR_GET_E_RSC(hp->h_err)); 1063*0Sstevel@tonic-gate break; 1064*0Sstevel@tonic-gate 1065*0Sstevel@tonic-gate case ESBD_SUSPEND: 1066*0Sstevel@tonic-gate PR_ALL("Non-suspendable devices (major #): %s\n", 1067*0Sstevel@tonic-gate DR_GET_E_RSC(hp->h_err)); 1068*0Sstevel@tonic-gate break; 1069*0Sstevel@tonic-gate 1070*0Sstevel@tonic-gate case ESBD_RESUME: 1071*0Sstevel@tonic-gate PR_ALL("Could not resume devices (major #): %s\n", 1072*0Sstevel@tonic-gate DR_GET_E_RSC(hp->h_err)); 1073*0Sstevel@tonic-gate break; 1074*0Sstevel@tonic-gate 1075*0Sstevel@tonic-gate case ESBD_KTHREAD: 1076*0Sstevel@tonic-gate PR_ALL("psmerr is ESBD_KTHREAD\n"); 1077*0Sstevel@tonic-gate break; 1078*0Sstevel@tonic-gate 1079*0Sstevel@tonic-gate case ESBD_NOERROR: 1080*0Sstevel@tonic-gate PR_ALL("sbd_error_t error code not set\n"); 1081*0Sstevel@tonic-gate break; 1082*0Sstevel@tonic-gate 1083*0Sstevel@tonic-gate default: 1084*0Sstevel@tonic-gate PR_ALL("Unknown error psmerr = %d\n", psmerr); 1085*0Sstevel@tonic-gate break; 1086*0Sstevel@tonic-gate } 1087*0Sstevel@tonic-gate } 1088*0Sstevel@tonic-gate dr_release_sr_handle(srh); 1089*0Sstevel@tonic-gate 1090*0Sstevel@tonic-gate return (0); 1091*0Sstevel@tonic-gate } 1092*0Sstevel@tonic-gate 1093*0Sstevel@tonic-gate /* 1094*0Sstevel@tonic-gate * Add a new integer value to the end of an array. Don't allow duplicates to 1095*0Sstevel@tonic-gate * appear in the array, and don't allow the array to overflow. Return the new 1096*0Sstevel@tonic-gate * total number of entries in the array. 1097*0Sstevel@tonic-gate */ 1098*0Sstevel@tonic-gate static int 1099*0Sstevel@tonic-gate dr_add_int(uint64_t *arr, int idx, int len, uint64_t val) 1100*0Sstevel@tonic-gate { 1101*0Sstevel@tonic-gate int i; 1102*0Sstevel@tonic-gate 1103*0Sstevel@tonic-gate if (arr == NULL) 1104*0Sstevel@tonic-gate return (0); 1105*0Sstevel@tonic-gate 1106*0Sstevel@tonic-gate if (idx >= len) 1107*0Sstevel@tonic-gate return (idx); 1108*0Sstevel@tonic-gate 1109*0Sstevel@tonic-gate for (i = 0; i < idx; i++) { 1110*0Sstevel@tonic-gate if (arr[i] == val) 1111*0Sstevel@tonic-gate return (idx); 1112*0Sstevel@tonic-gate } 1113*0Sstevel@tonic-gate 1114*0Sstevel@tonic-gate arr[idx++] = val; 1115*0Sstevel@tonic-gate 1116*0Sstevel@tonic-gate return (idx); 1117*0Sstevel@tonic-gate } 1118*0Sstevel@tonic-gate 1119*0Sstevel@tonic-gate /* 1120*0Sstevel@tonic-gate * Construct an sbd_error_t featuring a string representation of an array of 1121*0Sstevel@tonic-gate * integers as its e_rsc. 1122*0Sstevel@tonic-gate */ 1123*0Sstevel@tonic-gate static sbd_error_t * 1124*0Sstevel@tonic-gate drerr_int(int e_code, uint64_t *arr, int idx, int majors) 1125*0Sstevel@tonic-gate { 1126*0Sstevel@tonic-gate int i, n, buf_len, buf_idx, buf_avail; 1127*0Sstevel@tonic-gate char *dname; 1128*0Sstevel@tonic-gate char *buf; 1129*0Sstevel@tonic-gate sbd_error_t *new_sbd_err; 1130*0Sstevel@tonic-gate static char s_ellipsis[] = "..."; 1131*0Sstevel@tonic-gate 1132*0Sstevel@tonic-gate if (arr == NULL || idx <= 0) 1133*0Sstevel@tonic-gate return (NULL); 1134*0Sstevel@tonic-gate 1135*0Sstevel@tonic-gate /* MAXPATHLEN is the size of the e_rsc field in sbd_error_t. */ 1136*0Sstevel@tonic-gate buf = (char *)kmem_zalloc(MAXPATHLEN, KM_SLEEP); 1137*0Sstevel@tonic-gate 1138*0Sstevel@tonic-gate /* 1139*0Sstevel@tonic-gate * This is the total working area of the buffer. It must be computed 1140*0Sstevel@tonic-gate * as the size of 'buf', minus reserved space for the null terminator 1141*0Sstevel@tonic-gate * and the ellipsis string. 1142*0Sstevel@tonic-gate */ 1143*0Sstevel@tonic-gate buf_len = MAXPATHLEN - (strlen(s_ellipsis) + 1); 1144*0Sstevel@tonic-gate 1145*0Sstevel@tonic-gate /* Construct a string representation of the array values */ 1146*0Sstevel@tonic-gate for (buf_idx = 0, i = 0; i < idx; i++) { 1147*0Sstevel@tonic-gate buf_avail = buf_len - buf_idx; 1148*0Sstevel@tonic-gate if (majors) { 1149*0Sstevel@tonic-gate dname = ddi_major_to_name(arr[i]); 1150*0Sstevel@tonic-gate if (dname) { 1151*0Sstevel@tonic-gate n = snprintf(&buf[buf_idx], buf_avail, 1152*0Sstevel@tonic-gate "%s, ", dname); 1153*0Sstevel@tonic-gate } else { 1154*0Sstevel@tonic-gate n = snprintf(&buf[buf_idx], buf_avail, 1155*0Sstevel@tonic-gate "major %llu, ", arr[i]); 1156*0Sstevel@tonic-gate } 1157*0Sstevel@tonic-gate } else { 1158*0Sstevel@tonic-gate n = snprintf(&buf[buf_idx], buf_avail, "%llu, ", 1159*0Sstevel@tonic-gate arr[i]); 1160*0Sstevel@tonic-gate } 1161*0Sstevel@tonic-gate 1162*0Sstevel@tonic-gate /* An ellipsis gets appended when no more values fit */ 1163*0Sstevel@tonic-gate if (n >= buf_avail) { 1164*0Sstevel@tonic-gate (void) strcpy(&buf[buf_idx], s_ellipsis); 1165*0Sstevel@tonic-gate break; 1166*0Sstevel@tonic-gate } 1167*0Sstevel@tonic-gate 1168*0Sstevel@tonic-gate buf_idx += n; 1169*0Sstevel@tonic-gate } 1170*0Sstevel@tonic-gate 1171*0Sstevel@tonic-gate /* If all the contents fit, remove the trailing comma */ 1172*0Sstevel@tonic-gate if (n < buf_avail) { 1173*0Sstevel@tonic-gate buf[--buf_idx] = '\0'; 1174*0Sstevel@tonic-gate buf[--buf_idx] = '\0'; 1175*0Sstevel@tonic-gate } 1176*0Sstevel@tonic-gate 1177*0Sstevel@tonic-gate /* Return an sbd_error_t with the buffer and e_code */ 1178*0Sstevel@tonic-gate new_sbd_err = drerr_new(1, e_code, buf); 1179*0Sstevel@tonic-gate kmem_free(buf, MAXPATHLEN); 1180*0Sstevel@tonic-gate return (new_sbd_err); 1181*0Sstevel@tonic-gate } 1182