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