⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 swsusp.c.bak

📁 电源管理代码 基于linux2.6.10!apm方式的电源管理!很好的资料!做arm平台使用
💻 BAK
📖 第 1 页 / 共 2 页
字号:
/* * linux/kernel/power/swsusp.c * * This file is to realize architecture-independent * machine suspend feature using pretty near only high-level routines * * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu> * Copyright (C) 1998,2001-2004 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 * * More state savers are welcome. Especially for the scsi layer... * * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt */#include <linux/module.h>#include <linux/mm.h>#include <linux/suspend.h>#include <linux/smp_lock.h>#include <linux/file.h>#include <linux/utsname.h>#include <linux/version.h>#include <linux/delay.h>#include <linux/reboot.h>#include <linux/bitops.h>#include <linux/vt_kern.h>#include <linux/kbd_kern.h>#include <linux/keyboard.h>#include <linux/spinlock.h>#include <linux/genhd.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/swap.h>#include <linux/pm.h>#include <linux/device.h>#include <linux/buffer_head.h>#include <linux/swapops.h>#include <linux/bootmem.h>#include <linux/syscalls.h>#include <linux/console.h>#include <linux/highmem.h>#include <linux/bio.h>#include <asm/uaccess.h>#include <asm/mmu_context.h>#include <asm/pgtable.h>#include <asm/io.h>#include "power.h"/* References to section boundaries */extern char __nosave_begin, __nosave_end;extern int is_head_of_free_region(struct page *);/* Variables to be preserved over suspend */int pagedir_order_check;int nr_copy_pages_check;extern char resume_file[];static dev_t resume_device;/* Local variables that should not be affected by save */unsigned int nr_copy_pages __nosavedata = 0;/* Suspend pagedir is allocated before final copy, therefore it   must be freed after resume    Warning: this is evil. There are actually two pagedirs at time of   resume. One is "pagedir_save", which is empty frame allocated at   time of suspend, that must be freed. Second is "pagedir_nosave",    allocated at time of resume, that travels through memory not to   collide with anything.   Warning: this is even more evil than it seems. Pagedirs this file   talks about are completely different from page directories used by   MMU hardware. */suspend_pagedir_t *pagedir_nosave __nosavedata = NULL;static suspend_pagedir_t *pagedir_save;static int pagedir_order __nosavedata = 0;#define SWSUSP_SIG	"S1SUSPEND"struct swsusp_header {	char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)];	swp_entry_t swsusp_info;	char	orig_sig[10];	char	sig[10];} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;struct swsusp_info swsusp_info;/* * XXX: We try to keep some more pages free so that I/O operations succeed * without paging. Might this be more? */#define PAGES_FOR_IO	512/* * Saving part... *//* We memorize in swapfile_used what swap devices are used for suspension */#define SWAPFILE_UNUSED    0#define SWAPFILE_SUSPEND   1	/* This is the suspending device */#define SWAPFILE_IGNORED   2	/* Those are other swap devices ignored for suspension */static unsigned short swapfile_used[MAX_SWAPFILES];static unsigned short root_swap;static int mark_swapfiles(swp_entry_t prev){	int error;	rw_swap_page_sync(READ, 			  swp_entry(root_swap, 0),			  virt_to_page((unsigned long)&swsusp_header));	if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) ||	    !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {		memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);		memcpy(swsusp_header.sig,SWSUSP_SIG, 10);		swsusp_header.swsusp_info = prev;		error = rw_swap_page_sync(WRITE, 					  swp_entry(root_swap, 0),					  virt_to_page((unsigned long)						       &swsusp_header));	} else {		pr_debug("swsusp: Partition is not swap space.\n");		error = -ENODEV;	}	return error;}/* * Check whether the swap device is the specified resume * device, irrespective of whether they are specified by * identical names. * * (Thus, device inode aliasing is allowed.  You can say /dev/hda4 * instead of /dev/ide/host0/bus0/target0/lun0/part4 [if using devfs] * and they'll be considered the same device.  This is *necessary* for * devfs, since the resume code can only recognize the form /dev/hda4, * but the suspend code would see the long name.) */static int is_resume_device(const struct swap_info_struct *swap_info){	struct file *file = swap_info->swap_file;	struct inode *inode = file->f_dentry->d_inode;	return S_ISBLK(inode->i_mode) &&		resume_device == MKDEV(imajor(inode), iminor(inode));}int swsusp_swap_check(void) /* This is called before saving image */{	int i, len;		len=strlen(resume_file);	root_swap = 0xFFFF;		swap_list_lock();	for(i=0; i<MAX_SWAPFILES; i++) {		if (swap_info[i].flags == 0) {			swapfile_used[i]=SWAPFILE_UNUSED;		} else {			if(!len) {	    			printk(KERN_WARNING "resume= option should be used to set suspend device" );				if(root_swap == 0xFFFF) {					swapfile_used[i] = SWAPFILE_SUSPEND;					root_swap = i;				} else					swapfile_used[i] = SWAPFILE_IGNORED;				  			} else {	  			/* we ignore all swap devices that are not the resume_file */				if (is_resume_device(&swap_info[i])) {					swapfile_used[i] = SWAPFILE_SUSPEND;					root_swap = i;				} else {				  	swapfile_used[i] = SWAPFILE_IGNORED;				}			}		}	}	swap_list_unlock();	return (root_swap != 0xffff) ? 0 : -ENODEV;}/** * This is called after saving image so modification * will be lost after resume... and that's what we want. * we make the device unusable. A new call to * lock_swapdevices can unlock the devices.  */static void lock_swapdevices(void){	int i;	swap_list_lock();	for(i = 0; i< MAX_SWAPFILES; i++)		if(swapfile_used[i] == SWAPFILE_IGNORED) {			swap_info[i].flags ^= 0xFF;		}	swap_list_unlock();}/** *	write_swap_page - Write one page to a fresh swap location. *	@addr:	Address we're writing. *	@loc:	Place to store the entry we used. * *	Allocate a new swap entry and 'sync' it. Note we discard -EIO *	errors. That is an artifact left over from swsusp. It did not  *	check the return of rw_swap_page_sync() at all, since most pages *	written back to swap would return -EIO. *	This is a partial improvement, since we will at least return other *	errors, though we need to eventually fix the damn code. */static int write_page(unsigned long addr, swp_entry_t * loc){	swp_entry_t entry;	int error = 0;	entry = get_swap_page();	if (swp_offset(entry) && 	    swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) {		error = rw_swap_page_sync(WRITE, entry,					  virt_to_page(addr));		if (error == -EIO)			error = 0;		if (!error)			*loc = entry;	} else		error = -ENOSPC;	return error;}/** *	data_free - Free the swap entries used by the saved image. * *	Walk the list of used swap entries and free each one.  *	This is only used for cleanup when suspend fails. */static void data_free(void){	swp_entry_t entry;	int i;	for (i = 0; i < nr_copy_pages; i++) {		entry = (pagedir_nosave + i)->swap_address;		if (entry.val)			swap_free(entry);		else			break;		(pagedir_nosave + i)->swap_address = (swp_entry_t){0};	}}/** *	data_write - Write saved image to swap. * *	Walk the list of pages in the image and sync each one to swap. */static int data_write(void){	int error = 0;	int i;	unsigned int mod = nr_copy_pages / 100;	if (!mod)		mod = 1;	printk( "Writing data to swap (%d pages)...     ", nr_copy_pages );	for (i = 0; i < nr_copy_pages && !error; i++) {		if (!(i%mod))			printk( "\b\b\b\b%3d%%", i / mod );		error = write_page((pagedir_nosave+i)->address,					  &((pagedir_nosave+i)->swap_address));	}	printk("\b\b\b\bdone\n");	return error;}static void dump_info(void){	pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code);	pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages);	pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname);	pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename);	pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release);	pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version);	pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine);	pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname);	pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus);	pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages);	pr_debug(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages);}static void init_header(void){	memset(&swsusp_info,0,sizeof(swsusp_info));	swsusp_info.version_code = LINUX_VERSION_CODE;	swsusp_info.num_physpages = num_physpages;	memcpy(&swsusp_info.uts,&system_utsname,sizeof(system_utsname));	swsusp_info.suspend_pagedir = pagedir_nosave;	swsusp_info.cpus = num_online_cpus();	swsusp_info.image_pages = nr_copy_pages;	dump_info();}static int close_swap(void){	swp_entry_t entry;	int error;	error = write_page((unsigned long)&swsusp_info,&entry);	if (!error) { 		printk( "S" );		error = mark_swapfiles(entry);		printk( "|\n" );	}	return error;}/** *	free_pagedir_entries - Free pages used by the page directory. * *	This is used during suspend for error recovery. */static void free_pagedir_entries(void){	int i;	for (i = 0; i < swsusp_info.pagedir_pages; i++)		swap_free(swsusp_info.pagedir[i]);}/** *	write_pagedir - Write the array of pages holding the page directory. *	@last:	Last swap entry we write (needed for header). */static int write_pagedir(void){	unsigned long addr = (unsigned long)pagedir_nosave;	int error = 0;	int n = SUSPEND_PD_PAGES(nr_copy_pages);	int i;	swsusp_info.pagedir_pages = n;	printk( "Writing pagedir (%d pages)\n", n);	for (i = 0; i < n && !error; i++, addr += PAGE_SIZE)		error = write_page(addr, &swsusp_info.pagedir[i]);	return error;}/** *	write_suspend_image - Write entire image and metadata. * */static int write_suspend_image(void){	int error;	init_header();	if ((error = data_write()))		goto FreeData;	if ((error = write_pagedir()))		goto FreePagedir;	if ((error = close_swap()))		goto FreePagedir; Done:	return error; FreePagedir:	free_pagedir_entries(); FreeData:	data_free();	goto Done;}#ifdef CONFIG_HIGHMEMstruct highmem_page {	char *data;	struct page *page;	struct highmem_page *next;};struct highmem_page *highmem_copy = NULL;static int save_highmem_zone(struct zone *zone){	unsigned long zone_pfn;	for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {		struct page *page;		struct highmem_page *save;		void *kaddr;		unsigned long pfn = zone_pfn + zone->zone_start_pfn;		int chunk_size;		if (!(pfn%1000))			printk(".");		if (!pfn_valid(pfn))			continue;		page = pfn_to_page(pfn);		/*		 * This condition results from rvmalloc() sans vmalloc_32()		 * and architectural memory reservations. This should be		 * corrected eventually when the cases giving rise to this		 * are better understood.		 */		if (PageReserved(page)) {			printk("highmem reserved page?!\n");			continue;		}		if ((chunk_size = is_head_of_free_region(page))) {			pfn += chunk_size - 1;			zone_pfn += chunk_size - 1;			continue;		}		save = kmalloc(sizeof(struct highmem_page), GFP_ATOMIC);		if (!save)			return -ENOMEM;		save->next = highmem_copy;		save->page = page;		save->data = (void *) get_zeroed_page(GFP_ATOMIC);		if (!save->data) {			kfree(save);			return -ENOMEM;		}		kaddr = kmap_atomic(page, KM_USER0);		memcpy(save->data, kaddr, PAGE_SIZE);		kunmap_atomic(kaddr, KM_USER0);		highmem_copy = save;	}	return 0;}#endif /* CONFIG_HIGHMEM */static int save_highmem(void){#ifdef CONFIG_HIGHMEM	struct zone *zone;	int res = 0;	pr_debug("swsusp: Saving Highmem\n");	for_each_zone(zone) {		if (is_highmem(zone))			res = save_highmem_zone(zone);		if (res)			return res;	}#endif	return 0;}static int restore_highmem(void){#ifdef CONFIG_HIGHMEM	printk("swsusp: Restoring Highmem\n");	while (highmem_copy) {		struct highmem_page *save = highmem_copy;		void *kaddr;		highmem_copy = save->next;		kaddr = kmap_atomic(save->page, KM_USER0);		memcpy(kaddr, save->data, PAGE_SIZE);		kunmap_atomic(kaddr, KM_USER0);		free_page((long) save->data);		kfree(save);	}#endif	return 0;}static int pfn_is_nosave(unsigned long pfn){	unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;	unsigned long nosave_end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT;	return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);}/** *	saveable - Determine whether a page should be cloned or not. *	@pfn:	The page * *	We save a page if it's Reserved, and not in the range of pages *	statically defined as 'unsaveable', or if it isn't reserved, and *	isn't part of a free chunk of pages. *	If it is part of a free chunk, we update @pfn to point to the last  *	page of the chunk. */static int saveable(struct zone * zone, unsigned long * zone_pfn){	unsigned long pfn = *zone_pfn + zone->zone_start_pfn;	unsigned long chunk_size;	struct page * page;	if (!pfn_valid(pfn))		return 0;	if (!(pfn%1000))		printk(".");	page = pfn_to_page(pfn);	BUG_ON(PageReserved(page) && PageNosave(page));	if (PageNosave(page))		return 0;	if (PageReserved(page) && pfn_is_nosave(pfn)) {		pr_debug("[nosave pfn 0x%lx]", pfn);		return 0;	}	if ((chunk_size = is_head_of_free_region(page))) {		*zone_pfn += chunk_size - 1;		return 0;	}	return 1;}static void count_data_pages(void){	struct zone *zone;	unsigned long zone_pfn;	nr_copy_pages = 0;	for_each_zone(zone) {		if (!is_highmem(zone)) {			for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)				nr_copy_pages += saveable(zone, &zone_pfn);		}	}}static void copy_data_pages(void){	struct zone *zone;	unsigned long zone_pfn;	struct pbe * pbe = pagedir_nosave;		for_each_zone(zone) {		if (!is_highmem(zone))			for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {				if (saveable(zone, &zone_pfn)) {					struct page * page;					page = pfn_to_page(zone_pfn + zone->zone_start_pfn);					pbe->orig_address = (long) page_address(page);					/* copy_page is no usable for copying task structs. */					memcpy((void *)pbe->address, (void *)pbe->orig_address, PAGE_SIZE);					pbe++;				}			}	}}static void free_suspend_pagedir_zone(struct zone *zone, unsigned long pagedir){	unsigned long zone_pfn, pagedir_end, pagedir_pfn, pagedir_end_pfn;	pagedir_end = pagedir + (PAGE_SIZE << pagedir_order);	pagedir_pfn = __pa(pagedir) >> PAGE_SHIFT;	pagedir_end_pfn = __pa(pagedir_end) >> PAGE_SHIFT;	for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {		struct page *page;		unsigned long pfn = zone_pfn + zone->zone_start_pfn;		if (!pfn_valid(pfn))			continue;		page = pfn_to_page(pfn);		if (!TestClearPageNosave(page))			continue;		else if (pfn >= pagedir_pfn && pfn < pagedir_end_pfn)			continue;		__free_page(page);	}}void swsusp_free(void){	unsigned long p = (unsigned long)pagedir_save;	struct zone *zone;	for_each_zone(zone) {		if (!is_highmem(zone))			free_suspend_pagedir_zone(zone, p);	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -