| /* | 
 |  * linux/kernel/power/swsusp.c | 
 |  * | 
 |  * This file provides code to write suspend image to swap and read it back. | 
 |  * | 
 |  * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu> | 
 |  * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz> | 
 |  * | 
 |  * This file is released under the GPLv2. | 
 |  * | 
 |  * I'd like to thank the following people for their work: | 
 |  * | 
 |  * Pavel Machek <pavel@ucw.cz>: | 
 |  * Modifications, defectiveness pointing, being with me at the very beginning, | 
 |  * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. | 
 |  * | 
 |  * Steve Doddi <dirk@loth.demon.co.uk>: | 
 |  * Support the possibility of hardware state restoring. | 
 |  * | 
 |  * Raph <grey.havens@earthling.net>: | 
 |  * Support for preserving states of network devices and virtual console | 
 |  * (including X and svgatextmode) | 
 |  * | 
 |  * Kurt Garloff <garloff@suse.de>: | 
 |  * Straightened the critical function in order to prevent compilers from | 
 |  * playing tricks with local variables. | 
 |  * | 
 |  * Andreas Mohr <a.mohr@mailto.de> | 
 |  * | 
 |  * Alex Badea <vampire@go.ro>: | 
 |  * Fixed runaway init | 
 |  * | 
 |  * Rafael J. Wysocki <rjw@sisk.pl> | 
 |  * Reworked the freeing of memory and the handling of swap | 
 |  * | 
 |  * More state savers are welcome. Especially for the scsi layer... | 
 |  * | 
 |  * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt | 
 |  */ | 
 |  | 
 | #include <linux/mm.h> | 
 | #include <linux/suspend.h> | 
 | #include <linux/spinlock.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/major.h> | 
 | #include <linux/swap.h> | 
 | #include <linux/pm.h> | 
 | #include <linux/swapops.h> | 
 | #include <linux/bootmem.h> | 
 | #include <linux/syscalls.h> | 
 | #include <linux/highmem.h> | 
 | #include <linux/time.h> | 
 | #include <linux/rbtree.h> | 
 | #include <linux/io.h> | 
 |  | 
 | #include "power.h" | 
 |  | 
 | int in_suspend __nosavedata = 0; | 
 |  | 
 | /** | 
 |  *	The following functions are used for tracing the allocated | 
 |  *	swap pages, so that they can be freed in case of an error. | 
 |  */ | 
 |  | 
 | struct swsusp_extent { | 
 | 	struct rb_node node; | 
 | 	unsigned long start; | 
 | 	unsigned long end; | 
 | }; | 
 |  | 
 | static struct rb_root swsusp_extents = RB_ROOT; | 
 |  | 
 | static int swsusp_extents_insert(unsigned long swap_offset) | 
 | { | 
 | 	struct rb_node **new = &(swsusp_extents.rb_node); | 
 | 	struct rb_node *parent = NULL; | 
 | 	struct swsusp_extent *ext; | 
 |  | 
 | 	/* Figure out where to put the new node */ | 
 | 	while (*new) { | 
 | 		ext = container_of(*new, struct swsusp_extent, node); | 
 | 		parent = *new; | 
 | 		if (swap_offset < ext->start) { | 
 | 			/* Try to merge */ | 
 | 			if (swap_offset == ext->start - 1) { | 
 | 				ext->start--; | 
 | 				return 0; | 
 | 			} | 
 | 			new = &((*new)->rb_left); | 
 | 		} else if (swap_offset > ext->end) { | 
 | 			/* Try to merge */ | 
 | 			if (swap_offset == ext->end + 1) { | 
 | 				ext->end++; | 
 | 				return 0; | 
 | 			} | 
 | 			new = &((*new)->rb_right); | 
 | 		} else { | 
 | 			/* It already is in the tree */ | 
 | 			return -EINVAL; | 
 | 		} | 
 | 	} | 
 | 	/* Add the new node and rebalance the tree. */ | 
 | 	ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL); | 
 | 	if (!ext) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ext->start = swap_offset; | 
 | 	ext->end = swap_offset; | 
 | 	rb_link_node(&ext->node, parent, new); | 
 | 	rb_insert_color(&ext->node, &swsusp_extents); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /** | 
 |  *	alloc_swapdev_block - allocate a swap page and register that it has | 
 |  *	been allocated, so that it can be freed in case of an error. | 
 |  */ | 
 |  | 
 | sector_t alloc_swapdev_block(int swap) | 
 | { | 
 | 	unsigned long offset; | 
 |  | 
 | 	offset = swp_offset(get_swap_page_of_type(swap)); | 
 | 	if (offset) { | 
 | 		if (swsusp_extents_insert(offset)) | 
 | 			swap_free(swp_entry(swap, offset)); | 
 | 		else | 
 | 			return swapdev_block(swap, offset); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /** | 
 |  *	free_all_swap_pages - free swap pages allocated for saving image data. | 
 |  *	It also frees the extents used to register which swap entres had been | 
 |  *	allocated. | 
 |  */ | 
 |  | 
 | void free_all_swap_pages(int swap) | 
 | { | 
 | 	struct rb_node *node; | 
 |  | 
 | 	while ((node = swsusp_extents.rb_node)) { | 
 | 		struct swsusp_extent *ext; | 
 | 		unsigned long offset; | 
 |  | 
 | 		ext = container_of(node, struct swsusp_extent, node); | 
 | 		rb_erase(node, &swsusp_extents); | 
 | 		for (offset = ext->start; offset <= ext->end; offset++) | 
 | 			swap_free(swp_entry(swap, offset)); | 
 |  | 
 | 		kfree(ext); | 
 | 	} | 
 | } | 
 |  | 
 | int swsusp_swap_in_use(void) | 
 | { | 
 | 	return (swsusp_extents.rb_node != NULL); | 
 | } | 
 |  | 
 | /** | 
 |  *	swsusp_show_speed - print the time elapsed between two events represented by | 
 |  *	@start and @stop | 
 |  * | 
 |  *	@nr_pages -	number of pages processed between @start and @stop | 
 |  *	@msg -		introductory message to print | 
 |  */ | 
 |  | 
 | void swsusp_show_speed(struct timeval *start, struct timeval *stop, | 
 | 			unsigned nr_pages, char *msg) | 
 | { | 
 | 	s64 elapsed_centisecs64; | 
 | 	int centisecs; | 
 | 	int k; | 
 | 	int kps; | 
 |  | 
 | 	elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start); | 
 | 	do_div(elapsed_centisecs64, NSEC_PER_SEC / 100); | 
 | 	centisecs = elapsed_centisecs64; | 
 | 	if (centisecs == 0) | 
 | 		centisecs = 1;	/* avoid div-by-zero */ | 
 | 	k = nr_pages * (PAGE_SIZE / 1024); | 
 | 	kps = (k * 100) / centisecs; | 
 | 	printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", | 
 | 			msg, k, | 
 | 			centisecs / 100, centisecs % 100, | 
 | 			kps / 1000, (kps % 1000) / 10); | 
 | } |