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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * CPR driver support routines 30 */ 31 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <sys/kmem.h> 35 #include <sys/systm.h> 36 #include <sys/sunddi.h> 37 #include <sys/ddi_impldefs.h> 38 #include <sys/epm.h> 39 #include <sys/cpr.h> 40 41 #define CPR_BUFSIZE 128 42 43 extern int devi_detach(dev_info_t *, int); 44 extern int devi_attach(dev_info_t *, int); 45 46 static char *devi_string(dev_info_t *, char *); 47 static int cpr_is_real_device(dev_info_t *); 48 /* 49 * Xen uses this code to suspend _all_ drivers quickly and easily. 50 * Suspend and Resume uses it for the same reason, but also has 51 * to contend with some platform specific code that Xen does not. 52 * it is also used as a test entry point for developers/testers to 53 * execute code without going through a complete suspend. So additions 54 * that have platform implications shall need #if[n]def's. 55 */ 56 #ifndef __xpv 57 extern void i_cpr_save_configuration(dev_info_t *); 58 extern void i_cpr_restore_configuration(dev_info_t *); 59 #endif 60 61 /* 62 * Traverse the dev info tree: 63 * Call each device driver in the system via a special case 64 * of the detach() entry point to quiesce itself. 65 * Suspend children first. 66 * 67 * We only suspend/resume real devices. 68 */ 69 70 int 71 cpr_suspend_devices(dev_info_t *dip) 72 { 73 int error; 74 char buf[CPR_BUFSIZE]; 75 76 for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 77 if (cpr_suspend_devices(ddi_get_child(dip))) 78 return (ENXIO); 79 if (!cpr_is_real_device(dip)) 80 continue; 81 CPR_DEBUG(CPR_DEBUG2, "Suspending device %s\n", 82 devi_string(dip, buf)); 83 ASSERT((DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED) == 0); 84 85 #ifndef __xpv 86 i_cpr_save_configuration(dip); 87 #endif 88 89 90 if (!i_ddi_devi_attached(dip)) { 91 error = DDI_FAILURE; 92 } else { 93 #ifndef __xpv 94 if (cpr_test_point != DEVICE_SUSPEND_TO_RAM || 95 (cpr_test_point == DEVICE_SUSPEND_TO_RAM && 96 cpr_device == ddi_driver_major(dip))) { 97 #endif 98 error = devi_detach(dip, DDI_SUSPEND); 99 #ifndef __xpv 100 } else { 101 error = DDI_SUCCESS; 102 } 103 #endif 104 } 105 106 if (error == DDI_SUCCESS) { 107 DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED; 108 } 109 110 else { 111 CPR_DEBUG(CPR_DEBUG2, 112 "WARNING: Unable to suspend device %s\n", 113 devi_string(dip, buf)); 114 cpr_err(CE_WARN, "Unable to suspend device %s.", 115 devi_string(dip, buf)); 116 cpr_err(CE_WARN, "Device is busy or does not " 117 "support suspend/resume."); 118 #ifndef __xpv 119 /* 120 * the device has failed to suspend however, 121 * if cpr_test_point == FORCE_SUSPEND_TO_RAM 122 * after putting out the warning message above, 123 * we carry on as if suspending the device had 124 * been successful 125 */ 126 if (cpr_test_point == FORCE_SUSPEND_TO_RAM) 127 DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED; 128 else 129 #endif 130 return (ENXIO); 131 } 132 } 133 return (0); 134 } 135 136 /* 137 * Traverse the dev info tree: 138 * Call each device driver in the system via a special case 139 * of the attach() entry point to restore itself. 140 * This is a little tricky because it has to reverse the traversal 141 * order of cpr_suspend_devices(). 142 */ 143 int 144 cpr_resume_devices(dev_info_t *start, int resume_failed) 145 { 146 dev_info_t *dip, *next, *last = NULL; 147 int did_suspend; 148 int error = resume_failed; 149 char buf[CPR_BUFSIZE]; 150 151 while (last != start) { 152 dip = start; 153 next = ddi_get_next_sibling(dip); 154 while (next != last) { 155 dip = next; 156 next = ddi_get_next_sibling(dip); 157 } 158 159 /* 160 * cpr is the only one that uses this field and the device 161 * itself hasn't resumed yet, there is no need to use a 162 * lock, even though kernel threads are active by now. 163 */ 164 did_suspend = DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED; 165 if (did_suspend) 166 DEVI(dip)->devi_cpr_flags &= ~DCF_CPR_SUSPENDED; 167 168 /* 169 * Always attempt to restore device configuration before 170 * attempting resume 171 */ 172 #ifndef __xpv 173 i_cpr_restore_configuration(dip); 174 #endif 175 176 /* 177 * There may be background attaches happening on devices 178 * that were not originally suspended by cpr, so resume 179 * only devices that were suspended by cpr. Also, stop 180 * resuming after the first resume failure, but traverse 181 * the entire tree to clear the suspend flag unless the 182 * FORCE_SUSPEND_TO_RAM test point is set. 183 */ 184 #ifndef __xpv 185 if (did_suspend && (!error || 186 cpr_test_point == FORCE_SUSPEND_TO_RAM)) { 187 #else 188 if (did_suspend && !error) { 189 #endif 190 CPR_DEBUG(CPR_DEBUG2, "Resuming device %s\n", 191 devi_string(dip, buf)); 192 /* 193 * If a device suspended by cpr gets detached during 194 * the resume process (for example, due to hotplugging) 195 * before cpr gets around to issuing it a DDI_RESUME, 196 * we'll have problems. 197 */ 198 if (!i_ddi_devi_attached(dip)) { 199 CPR_DEBUG(CPR_DEBUG2, "WARNING: Skipping " 200 "%s, device not ready for resume\n", 201 devi_string(dip, buf)); 202 cpr_err(CE_WARN, "Skipping %s, device " 203 "not ready for resume", 204 devi_string(dip, buf)); 205 #ifndef __xpv 206 } else if (cpr_test_point != DEVICE_SUSPEND_TO_RAM || 207 (cpr_test_point == DEVICE_SUSPEND_TO_RAM && 208 cpr_device == ddi_driver_major(dip))) { 209 #else 210 } else { 211 #endif 212 if (devi_attach(dip, DDI_RESUME) != 213 DDI_SUCCESS) { 214 error = ENXIO; 215 } 216 } 217 } 218 219 if (error == ENXIO) { 220 CPR_DEBUG(CPR_DEBUG2, 221 "WARNING: Unable to resume device %s\n", 222 devi_string(dip, buf)); 223 cpr_err(CE_WARN, "Unable to resume device %s", 224 devi_string(dip, buf)); 225 } 226 227 error = cpr_resume_devices(ddi_get_child(dip), error); 228 last = dip; 229 } 230 231 return (error); 232 } 233 234 /* 235 * Returns a string which contains device name and address. 236 */ 237 static char * 238 devi_string(dev_info_t *devi, char *buf) 239 { 240 char *name; 241 char *address; 242 int size; 243 244 name = ddi_node_name(devi); 245 address = ddi_get_name_addr(devi); 246 size = (name == NULL) ? strlen("<null name>") : strlen(name); 247 size += (address == NULL) ? strlen("<null>") : strlen(address); 248 249 /* 250 * Make sure that we don't over-run the buffer. 251 * There are 2 additional characters in the string. 252 */ 253 ASSERT((size + 2) <= CPR_BUFSIZE); 254 255 if (name == NULL) 256 (void) strcpy(buf, "<null name>"); 257 else 258 (void) strcpy(buf, name); 259 260 (void) strcat(buf, "@"); 261 if (address == NULL) 262 (void) strcat(buf, "<null>"); 263 else 264 (void) strcat(buf, address); 265 266 return (buf); 267 } 268 269 /* 270 * This function determines whether the given device is real (and should 271 * be suspended) or not (pseudo like). If the device has a "reg" property 272 * then it is presumed to have register state to save/restore. 273 */ 274 static int 275 cpr_is_real_device(dev_info_t *dip) 276 { 277 struct regspec *regbuf; 278 int length; 279 int rc; 280 281 if (ddi_get_driver(dip) == NULL) 282 return (0); 283 284 /* 285 * First those devices for which special arrangements have been made 286 */ 287 if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR)) 288 return (1); 289 if (DEVI(dip)->devi_pm_flags & PMC_NO_SR) 290 return (0); 291 292 /* 293 * now the general case 294 */ 295 rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 296 (caddr_t)®buf, &length); 297 ASSERT(rc != DDI_PROP_NO_MEMORY); 298 if (rc != DDI_PROP_SUCCESS) { 299 return (0); 300 } else { 301 kmem_free((caddr_t)regbuf, length); 302 return (1); 303 } 304 } 305