Home | History | Annotate | Download | only in cpr
      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)&regbuf, &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