xref: /openbmc/linux/kernel/power/swap.c (revision b595076a)
161159a31SRafael J. Wysocki /*
261159a31SRafael J. Wysocki  * linux/kernel/power/swap.c
361159a31SRafael J. Wysocki  *
461159a31SRafael J. Wysocki  * This file provides functions for reading the suspend image from
561159a31SRafael J. Wysocki  * and writing it to a swap partition.
661159a31SRafael J. Wysocki  *
7a2531293SPavel Machek  * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz>
861159a31SRafael J. Wysocki  * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
961159a31SRafael J. Wysocki  *
1061159a31SRafael J. Wysocki  * This file is released under the GPLv2.
1161159a31SRafael J. Wysocki  *
1261159a31SRafael J. Wysocki  */
1361159a31SRafael J. Wysocki 
1461159a31SRafael J. Wysocki #include <linux/module.h>
1561159a31SRafael J. Wysocki #include <linux/file.h>
1661159a31SRafael J. Wysocki #include <linux/delay.h>
1761159a31SRafael J. Wysocki #include <linux/bitops.h>
1861159a31SRafael J. Wysocki #include <linux/genhd.h>
1961159a31SRafael J. Wysocki #include <linux/device.h>
2061159a31SRafael J. Wysocki #include <linux/buffer_head.h>
2161159a31SRafael J. Wysocki #include <linux/bio.h>
22546e0d27SAndrew Morton #include <linux/blkdev.h>
2361159a31SRafael J. Wysocki #include <linux/swap.h>
2461159a31SRafael J. Wysocki #include <linux/swapops.h>
2561159a31SRafael J. Wysocki #include <linux/pm.h>
265a0e3ad6STejun Heo #include <linux/slab.h>
27f996fc96SBojan Smojver #include <linux/lzo.h>
28f996fc96SBojan Smojver #include <linux/vmalloc.h>
2961159a31SRafael J. Wysocki 
3061159a31SRafael J. Wysocki #include "power.h"
3161159a31SRafael J. Wysocki 
323624eb04SRafael J. Wysocki #define HIBERNATE_SIG	"LINHIB0001"
3361159a31SRafael J. Wysocki 
3451fb352bSJiri Slaby /*
3551fb352bSJiri Slaby  *	The swap map is a data structure used for keeping track of each page
3651fb352bSJiri Slaby  *	written to a swap partition.  It consists of many swap_map_page
3790133673SCesar Eduardo Barros  *	structures that contain each an array of MAP_PAGE_ENTRIES swap entries.
3851fb352bSJiri Slaby  *	These structures are stored on the swap and linked together with the
3951fb352bSJiri Slaby  *	help of the .next_swap member.
4051fb352bSJiri Slaby  *
4151fb352bSJiri Slaby  *	The swap map is created during suspend.  The swap map pages are
4251fb352bSJiri Slaby  *	allocated and populated one at a time, so we only need one memory
4351fb352bSJiri Slaby  *	page to set up the entire structure.
4451fb352bSJiri Slaby  *
4551fb352bSJiri Slaby  *	During resume we also only need to use one swap_map_page structure
4651fb352bSJiri Slaby  *	at a time.
4751fb352bSJiri Slaby  */
4851fb352bSJiri Slaby 
4951fb352bSJiri Slaby #define MAP_PAGE_ENTRIES	(PAGE_SIZE / sizeof(sector_t) - 1)
5051fb352bSJiri Slaby 
5151fb352bSJiri Slaby struct swap_map_page {
5251fb352bSJiri Slaby 	sector_t entries[MAP_PAGE_ENTRIES];
5351fb352bSJiri Slaby 	sector_t next_swap;
5451fb352bSJiri Slaby };
5551fb352bSJiri Slaby 
5651fb352bSJiri Slaby /**
5751fb352bSJiri Slaby  *	The swap_map_handle structure is used for handling swap in
5851fb352bSJiri Slaby  *	a file-alike way
5951fb352bSJiri Slaby  */
6051fb352bSJiri Slaby 
6151fb352bSJiri Slaby struct swap_map_handle {
6251fb352bSJiri Slaby 	struct swap_map_page *cur;
6351fb352bSJiri Slaby 	sector_t cur_swap;
6451fb352bSJiri Slaby 	sector_t first_sector;
6551fb352bSJiri Slaby 	unsigned int k;
6651fb352bSJiri Slaby };
6751fb352bSJiri Slaby 
681b29c164SVivek Goyal struct swsusp_header {
69a634cc10SRafael J. Wysocki 	char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int)];
703aef83e0SRafael J. Wysocki 	sector_t image;
71a634cc10SRafael J. Wysocki 	unsigned int flags;	/* Flags to pass to the "boot" kernel */
7261159a31SRafael J. Wysocki 	char	orig_sig[10];
7361159a31SRafael J. Wysocki 	char	sig[10];
741b29c164SVivek Goyal } __attribute__((packed));
751b29c164SVivek Goyal 
761b29c164SVivek Goyal static struct swsusp_header *swsusp_header;
7761159a31SRafael J. Wysocki 
780414f2ecSNigel Cunningham /**
790414f2ecSNigel Cunningham  *	The following functions are used for tracing the allocated
800414f2ecSNigel Cunningham  *	swap pages, so that they can be freed in case of an error.
810414f2ecSNigel Cunningham  */
820414f2ecSNigel Cunningham 
830414f2ecSNigel Cunningham struct swsusp_extent {
840414f2ecSNigel Cunningham 	struct rb_node node;
850414f2ecSNigel Cunningham 	unsigned long start;
860414f2ecSNigel Cunningham 	unsigned long end;
870414f2ecSNigel Cunningham };
880414f2ecSNigel Cunningham 
890414f2ecSNigel Cunningham static struct rb_root swsusp_extents = RB_ROOT;
900414f2ecSNigel Cunningham 
910414f2ecSNigel Cunningham static int swsusp_extents_insert(unsigned long swap_offset)
920414f2ecSNigel Cunningham {
930414f2ecSNigel Cunningham 	struct rb_node **new = &(swsusp_extents.rb_node);
940414f2ecSNigel Cunningham 	struct rb_node *parent = NULL;
950414f2ecSNigel Cunningham 	struct swsusp_extent *ext;
960414f2ecSNigel Cunningham 
970414f2ecSNigel Cunningham 	/* Figure out where to put the new node */
980414f2ecSNigel Cunningham 	while (*new) {
990414f2ecSNigel Cunningham 		ext = container_of(*new, struct swsusp_extent, node);
1000414f2ecSNigel Cunningham 		parent = *new;
1010414f2ecSNigel Cunningham 		if (swap_offset < ext->start) {
1020414f2ecSNigel Cunningham 			/* Try to merge */
1030414f2ecSNigel Cunningham 			if (swap_offset == ext->start - 1) {
1040414f2ecSNigel Cunningham 				ext->start--;
1050414f2ecSNigel Cunningham 				return 0;
1060414f2ecSNigel Cunningham 			}
1070414f2ecSNigel Cunningham 			new = &((*new)->rb_left);
1080414f2ecSNigel Cunningham 		} else if (swap_offset > ext->end) {
1090414f2ecSNigel Cunningham 			/* Try to merge */
1100414f2ecSNigel Cunningham 			if (swap_offset == ext->end + 1) {
1110414f2ecSNigel Cunningham 				ext->end++;
1120414f2ecSNigel Cunningham 				return 0;
1130414f2ecSNigel Cunningham 			}
1140414f2ecSNigel Cunningham 			new = &((*new)->rb_right);
1150414f2ecSNigel Cunningham 		} else {
1160414f2ecSNigel Cunningham 			/* It already is in the tree */
1170414f2ecSNigel Cunningham 			return -EINVAL;
1180414f2ecSNigel Cunningham 		}
1190414f2ecSNigel Cunningham 	}
1200414f2ecSNigel Cunningham 	/* Add the new node and rebalance the tree. */
1210414f2ecSNigel Cunningham 	ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL);
1220414f2ecSNigel Cunningham 	if (!ext)
1230414f2ecSNigel Cunningham 		return -ENOMEM;
1240414f2ecSNigel Cunningham 
1250414f2ecSNigel Cunningham 	ext->start = swap_offset;
1260414f2ecSNigel Cunningham 	ext->end = swap_offset;
1270414f2ecSNigel Cunningham 	rb_link_node(&ext->node, parent, new);
1280414f2ecSNigel Cunningham 	rb_insert_color(&ext->node, &swsusp_extents);
1290414f2ecSNigel Cunningham 	return 0;
1300414f2ecSNigel Cunningham }
1310414f2ecSNigel Cunningham 
1320414f2ecSNigel Cunningham /**
1330414f2ecSNigel Cunningham  *	alloc_swapdev_block - allocate a swap page and register that it has
1340414f2ecSNigel Cunningham  *	been allocated, so that it can be freed in case of an error.
1350414f2ecSNigel Cunningham  */
1360414f2ecSNigel Cunningham 
1370414f2ecSNigel Cunningham sector_t alloc_swapdev_block(int swap)
1380414f2ecSNigel Cunningham {
1390414f2ecSNigel Cunningham 	unsigned long offset;
1400414f2ecSNigel Cunningham 
141910321eaSHugh Dickins 	offset = swp_offset(get_swap_page_of_type(swap));
1420414f2ecSNigel Cunningham 	if (offset) {
1430414f2ecSNigel Cunningham 		if (swsusp_extents_insert(offset))
144910321eaSHugh Dickins 			swap_free(swp_entry(swap, offset));
1450414f2ecSNigel Cunningham 		else
1460414f2ecSNigel Cunningham 			return swapdev_block(swap, offset);
1470414f2ecSNigel Cunningham 	}
1480414f2ecSNigel Cunningham 	return 0;
1490414f2ecSNigel Cunningham }
1500414f2ecSNigel Cunningham 
1510414f2ecSNigel Cunningham /**
1520414f2ecSNigel Cunningham  *	free_all_swap_pages - free swap pages allocated for saving image data.
15390133673SCesar Eduardo Barros  *	It also frees the extents used to register which swap entries had been
1540414f2ecSNigel Cunningham  *	allocated.
1550414f2ecSNigel Cunningham  */
1560414f2ecSNigel Cunningham 
1570414f2ecSNigel Cunningham void free_all_swap_pages(int swap)
1580414f2ecSNigel Cunningham {
1590414f2ecSNigel Cunningham 	struct rb_node *node;
1600414f2ecSNigel Cunningham 
1610414f2ecSNigel Cunningham 	while ((node = swsusp_extents.rb_node)) {
1620414f2ecSNigel Cunningham 		struct swsusp_extent *ext;
1630414f2ecSNigel Cunningham 		unsigned long offset;
1640414f2ecSNigel Cunningham 
1650414f2ecSNigel Cunningham 		ext = container_of(node, struct swsusp_extent, node);
1660414f2ecSNigel Cunningham 		rb_erase(node, &swsusp_extents);
1670414f2ecSNigel Cunningham 		for (offset = ext->start; offset <= ext->end; offset++)
168910321eaSHugh Dickins 			swap_free(swp_entry(swap, offset));
1690414f2ecSNigel Cunningham 
1700414f2ecSNigel Cunningham 		kfree(ext);
1710414f2ecSNigel Cunningham 	}
1720414f2ecSNigel Cunningham }
1730414f2ecSNigel Cunningham 
1740414f2ecSNigel Cunningham int swsusp_swap_in_use(void)
1750414f2ecSNigel Cunningham {
1760414f2ecSNigel Cunningham 	return (swsusp_extents.rb_node != NULL);
1770414f2ecSNigel Cunningham }
1780414f2ecSNigel Cunningham 
17961159a31SRafael J. Wysocki /*
1803fc6b34fSRafael J. Wysocki  * General things
18161159a31SRafael J. Wysocki  */
18261159a31SRafael J. Wysocki 
18361159a31SRafael J. Wysocki static unsigned short root_swap = 0xffff;
1848a0d613fSJiri Slaby struct block_device *hib_resume_bdev;
1853fc6b34fSRafael J. Wysocki 
1863fc6b34fSRafael J. Wysocki /*
1873fc6b34fSRafael J. Wysocki  * Saving part
1883fc6b34fSRafael J. Wysocki  */
18961159a31SRafael J. Wysocki 
19051fb352bSJiri Slaby static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags)
19161159a31SRafael J. Wysocki {
19261159a31SRafael J. Wysocki 	int error;
19361159a31SRafael J. Wysocki 
1948a0d613fSJiri Slaby 	hib_bio_read_page(swsusp_resume_block, swsusp_header, NULL);
1951b29c164SVivek Goyal 	if (!memcmp("SWAP-SPACE",swsusp_header->sig, 10) ||
1961b29c164SVivek Goyal 	    !memcmp("SWAPSPACE2",swsusp_header->sig, 10)) {
1971b29c164SVivek Goyal 		memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10);
1983624eb04SRafael J. Wysocki 		memcpy(swsusp_header->sig, HIBERNATE_SIG, 10);
19951fb352bSJiri Slaby 		swsusp_header->image = handle->first_sector;
200a634cc10SRafael J. Wysocki 		swsusp_header->flags = flags;
2018a0d613fSJiri Slaby 		error = hib_bio_write_page(swsusp_resume_block,
2021b29c164SVivek Goyal 					swsusp_header, NULL);
20361159a31SRafael J. Wysocki 	} else {
20423976728SRafael J. Wysocki 		printk(KERN_ERR "PM: Swap header not found!\n");
20561159a31SRafael J. Wysocki 		error = -ENODEV;
20661159a31SRafael J. Wysocki 	}
20761159a31SRafael J. Wysocki 	return error;
20861159a31SRafael J. Wysocki }
20961159a31SRafael J. Wysocki 
21061159a31SRafael J. Wysocki /**
21161159a31SRafael J. Wysocki  *	swsusp_swap_check - check if the resume device is a swap device
21261159a31SRafael J. Wysocki  *	and get its index (if so)
2136f612af5SJiri Slaby  *
2146f612af5SJiri Slaby  *	This is called before saving image
21561159a31SRafael J. Wysocki  */
2166f612af5SJiri Slaby static int swsusp_swap_check(void)
21761159a31SRafael J. Wysocki {
2183aef83e0SRafael J. Wysocki 	int res;
21961159a31SRafael J. Wysocki 
2207bf23687SRafael J. Wysocki 	res = swap_type_of(swsusp_resume_device, swsusp_resume_block,
2218a0d613fSJiri Slaby 			&hib_resume_bdev);
2223aef83e0SRafael J. Wysocki 	if (res < 0)
2233aef83e0SRafael J. Wysocki 		return res;
2243aef83e0SRafael J. Wysocki 
22561159a31SRafael J. Wysocki 	root_swap = res;
2268a0d613fSJiri Slaby 	res = blkdev_get(hib_resume_bdev, FMODE_WRITE);
2277bf23687SRafael J. Wysocki 	if (res)
2287bf23687SRafael J. Wysocki 		return res;
2293aef83e0SRafael J. Wysocki 
2308a0d613fSJiri Slaby 	res = set_blocksize(hib_resume_bdev, PAGE_SIZE);
2313aef83e0SRafael J. Wysocki 	if (res < 0)
2328a0d613fSJiri Slaby 		blkdev_put(hib_resume_bdev, FMODE_WRITE);
2333aef83e0SRafael J. Wysocki 
23461159a31SRafael J. Wysocki 	return res;
23561159a31SRafael J. Wysocki }
23661159a31SRafael J. Wysocki 
23761159a31SRafael J. Wysocki /**
23861159a31SRafael J. Wysocki  *	write_page - Write one page to given swap location.
23961159a31SRafael J. Wysocki  *	@buf:		Address we're writing.
24061159a31SRafael J. Wysocki  *	@offset:	Offset of the swap page we're writing to.
241ab954160SAndrew Morton  *	@bio_chain:	Link the next write BIO here
24261159a31SRafael J. Wysocki  */
24361159a31SRafael J. Wysocki 
2443aef83e0SRafael J. Wysocki static int write_page(void *buf, sector_t offset, struct bio **bio_chain)
24561159a31SRafael J. Wysocki {
2463aef83e0SRafael J. Wysocki 	void *src;
24761159a31SRafael J. Wysocki 
2483aef83e0SRafael J. Wysocki 	if (!offset)
2493aef83e0SRafael J. Wysocki 		return -ENOSPC;
250ab954160SAndrew Morton 
251ab954160SAndrew Morton 	if (bio_chain) {
25285949121SRafael J. Wysocki 		src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
2533aef83e0SRafael J. Wysocki 		if (src) {
2543ecb01dfSJan Beulich 			copy_page(src, buf);
2553aef83e0SRafael J. Wysocki 		} else {
256ab954160SAndrew Morton 			WARN_ON_ONCE(1);
257ab954160SAndrew Morton 			bio_chain = NULL;	/* Go synchronous */
2583aef83e0SRafael J. Wysocki 			src = buf;
2593aef83e0SRafael J. Wysocki 		}
260ab954160SAndrew Morton 	} else {
2613aef83e0SRafael J. Wysocki 		src = buf;
262ab954160SAndrew Morton 	}
2638a0d613fSJiri Slaby 	return hib_bio_write_page(offset, src, bio_chain);
26461159a31SRafael J. Wysocki }
26561159a31SRafael J. Wysocki 
26661159a31SRafael J. Wysocki static void release_swap_writer(struct swap_map_handle *handle)
26761159a31SRafael J. Wysocki {
26861159a31SRafael J. Wysocki 	if (handle->cur)
26961159a31SRafael J. Wysocki 		free_page((unsigned long)handle->cur);
27061159a31SRafael J. Wysocki 	handle->cur = NULL;
27161159a31SRafael J. Wysocki }
27261159a31SRafael J. Wysocki 
27361159a31SRafael J. Wysocki static int get_swap_writer(struct swap_map_handle *handle)
27461159a31SRafael J. Wysocki {
2756f612af5SJiri Slaby 	int ret;
2766f612af5SJiri Slaby 
2776f612af5SJiri Slaby 	ret = swsusp_swap_check();
2786f612af5SJiri Slaby 	if (ret) {
2796f612af5SJiri Slaby 		if (ret != -ENOSPC)
2806f612af5SJiri Slaby 			printk(KERN_ERR "PM: Cannot find swap device, try "
2816f612af5SJiri Slaby 					"swapon -a.\n");
2826f612af5SJiri Slaby 		return ret;
2836f612af5SJiri Slaby 	}
28461159a31SRafael J. Wysocki 	handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
2856f612af5SJiri Slaby 	if (!handle->cur) {
2866f612af5SJiri Slaby 		ret = -ENOMEM;
2876f612af5SJiri Slaby 		goto err_close;
2886f612af5SJiri Slaby 	}
289d1d241ccSRafael J. Wysocki 	handle->cur_swap = alloc_swapdev_block(root_swap);
29061159a31SRafael J. Wysocki 	if (!handle->cur_swap) {
2916f612af5SJiri Slaby 		ret = -ENOSPC;
2926f612af5SJiri Slaby 		goto err_rel;
29361159a31SRafael J. Wysocki 	}
29461159a31SRafael J. Wysocki 	handle->k = 0;
29551fb352bSJiri Slaby 	handle->first_sector = handle->cur_swap;
29661159a31SRafael J. Wysocki 	return 0;
2976f612af5SJiri Slaby err_rel:
2986f612af5SJiri Slaby 	release_swap_writer(handle);
2996f612af5SJiri Slaby err_close:
3006f612af5SJiri Slaby 	swsusp_close(FMODE_WRITE);
3016f612af5SJiri Slaby 	return ret;
30261159a31SRafael J. Wysocki }
30361159a31SRafael J. Wysocki 
304ab954160SAndrew Morton static int swap_write_page(struct swap_map_handle *handle, void *buf,
305ab954160SAndrew Morton 				struct bio **bio_chain)
306ab954160SAndrew Morton {
307ab954160SAndrew Morton 	int error = 0;
3083aef83e0SRafael J. Wysocki 	sector_t offset;
30961159a31SRafael J. Wysocki 
31061159a31SRafael J. Wysocki 	if (!handle->cur)
31161159a31SRafael J. Wysocki 		return -EINVAL;
312d1d241ccSRafael J. Wysocki 	offset = alloc_swapdev_block(root_swap);
313ab954160SAndrew Morton 	error = write_page(buf, offset, bio_chain);
31461159a31SRafael J. Wysocki 	if (error)
31561159a31SRafael J. Wysocki 		return error;
31661159a31SRafael J. Wysocki 	handle->cur->entries[handle->k++] = offset;
31761159a31SRafael J. Wysocki 	if (handle->k >= MAP_PAGE_ENTRIES) {
3188a0d613fSJiri Slaby 		error = hib_wait_on_bio_chain(bio_chain);
319ab954160SAndrew Morton 		if (error)
320ab954160SAndrew Morton 			goto out;
321d1d241ccSRafael J. Wysocki 		offset = alloc_swapdev_block(root_swap);
32261159a31SRafael J. Wysocki 		if (!offset)
32361159a31SRafael J. Wysocki 			return -ENOSPC;
32461159a31SRafael J. Wysocki 		handle->cur->next_swap = offset;
325ab954160SAndrew Morton 		error = write_page(handle->cur, handle->cur_swap, NULL);
32661159a31SRafael J. Wysocki 		if (error)
327ab954160SAndrew Morton 			goto out;
3283ecb01dfSJan Beulich 		clear_page(handle->cur);
32961159a31SRafael J. Wysocki 		handle->cur_swap = offset;
33061159a31SRafael J. Wysocki 		handle->k = 0;
33161159a31SRafael J. Wysocki 	}
332ab954160SAndrew Morton  out:
333ab954160SAndrew Morton 	return error;
33461159a31SRafael J. Wysocki }
33561159a31SRafael J. Wysocki 
33661159a31SRafael J. Wysocki static int flush_swap_writer(struct swap_map_handle *handle)
33761159a31SRafael J. Wysocki {
33861159a31SRafael J. Wysocki 	if (handle->cur && handle->cur_swap)
339ab954160SAndrew Morton 		return write_page(handle->cur, handle->cur_swap, NULL);
34061159a31SRafael J. Wysocki 	else
34161159a31SRafael J. Wysocki 		return -EINVAL;
34261159a31SRafael J. Wysocki }
34361159a31SRafael J. Wysocki 
3446f612af5SJiri Slaby static int swap_writer_finish(struct swap_map_handle *handle,
3456f612af5SJiri Slaby 		unsigned int flags, int error)
3466f612af5SJiri Slaby {
3476f612af5SJiri Slaby 	if (!error) {
3486f612af5SJiri Slaby 		flush_swap_writer(handle);
3496f612af5SJiri Slaby 		printk(KERN_INFO "PM: S");
3506f612af5SJiri Slaby 		error = mark_swapfiles(handle, flags);
3516f612af5SJiri Slaby 		printk("|\n");
3526f612af5SJiri Slaby 	}
3536f612af5SJiri Slaby 
3546f612af5SJiri Slaby 	if (error)
3556f612af5SJiri Slaby 		free_all_swap_pages(root_swap);
3566f612af5SJiri Slaby 	release_swap_writer(handle);
3576f612af5SJiri Slaby 	swsusp_close(FMODE_WRITE);
3586f612af5SJiri Slaby 
3596f612af5SJiri Slaby 	return error;
3606f612af5SJiri Slaby }
3616f612af5SJiri Slaby 
362f996fc96SBojan Smojver /* We need to remember how much compressed data we need to read. */
363f996fc96SBojan Smojver #define LZO_HEADER	sizeof(size_t)
364f996fc96SBojan Smojver 
365f996fc96SBojan Smojver /* Number of pages/bytes we'll compress at one time. */
366f996fc96SBojan Smojver #define LZO_UNC_PAGES	32
367f996fc96SBojan Smojver #define LZO_UNC_SIZE	(LZO_UNC_PAGES * PAGE_SIZE)
368f996fc96SBojan Smojver 
369f996fc96SBojan Smojver /* Number of pages/bytes we need for compressed data (worst case). */
370f996fc96SBojan Smojver #define LZO_CMP_PAGES	DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \
371f996fc96SBojan Smojver 			             LZO_HEADER, PAGE_SIZE)
372f996fc96SBojan Smojver #define LZO_CMP_SIZE	(LZO_CMP_PAGES * PAGE_SIZE)
373f996fc96SBojan Smojver 
37461159a31SRafael J. Wysocki /**
37561159a31SRafael J. Wysocki  *	save_image - save the suspend image data
37661159a31SRafael J. Wysocki  */
37761159a31SRafael J. Wysocki 
37861159a31SRafael J. Wysocki static int save_image(struct swap_map_handle *handle,
37961159a31SRafael J. Wysocki                       struct snapshot_handle *snapshot,
3803a4f7577SAndrew Morton                       unsigned int nr_to_write)
38161159a31SRafael J. Wysocki {
38261159a31SRafael J. Wysocki 	unsigned int m;
38361159a31SRafael J. Wysocki 	int ret;
3843a4f7577SAndrew Morton 	int nr_pages;
385ab954160SAndrew Morton 	int err2;
386ab954160SAndrew Morton 	struct bio *bio;
3873a4f7577SAndrew Morton 	struct timeval start;
3883a4f7577SAndrew Morton 	struct timeval stop;
38961159a31SRafael J. Wysocki 
39023976728SRafael J. Wysocki 	printk(KERN_INFO "PM: Saving image data pages (%u pages) ...     ",
39123976728SRafael J. Wysocki 		nr_to_write);
3923a4f7577SAndrew Morton 	m = nr_to_write / 100;
39361159a31SRafael J. Wysocki 	if (!m)
39461159a31SRafael J. Wysocki 		m = 1;
39561159a31SRafael J. Wysocki 	nr_pages = 0;
396ab954160SAndrew Morton 	bio = NULL;
3973a4f7577SAndrew Morton 	do_gettimeofday(&start);
3984ff277f9SJiri Slaby 	while (1) {
399d3c1b24cSJiri Slaby 		ret = snapshot_read_next(snapshot);
4004ff277f9SJiri Slaby 		if (ret <= 0)
4014ff277f9SJiri Slaby 			break;
4024ff277f9SJiri Slaby 		ret = swap_write_page(handle, data_of(*snapshot), &bio);
4034ff277f9SJiri Slaby 		if (ret)
40461159a31SRafael J. Wysocki 			break;
40561159a31SRafael J. Wysocki 		if (!(nr_pages % m))
40666d0ae4dSJiri Slaby 			printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m);
40761159a31SRafael J. Wysocki 		nr_pages++;
40861159a31SRafael J. Wysocki 	}
4098a0d613fSJiri Slaby 	err2 = hib_wait_on_bio_chain(&bio);
4103a4f7577SAndrew Morton 	do_gettimeofday(&stop);
4114ff277f9SJiri Slaby 	if (!ret)
4124ff277f9SJiri Slaby 		ret = err2;
4134ff277f9SJiri Slaby 	if (!ret)
41466d0ae4dSJiri Slaby 		printk(KERN_CONT "\b\b\b\bdone\n");
4154ff277f9SJiri Slaby 	else
41666d0ae4dSJiri Slaby 		printk(KERN_CONT "\n");
4170d3a9abeSRafael J. Wysocki 	swsusp_show_speed(&start, &stop, nr_to_write, "Wrote");
4184ff277f9SJiri Slaby 	return ret;
41961159a31SRafael J. Wysocki }
42061159a31SRafael J. Wysocki 
421f996fc96SBojan Smojver 
422f996fc96SBojan Smojver /**
423f996fc96SBojan Smojver  * save_image_lzo - Save the suspend image data compressed with LZO.
424f996fc96SBojan Smojver  * @handle: Swap mam handle to use for saving the image.
425f996fc96SBojan Smojver  * @snapshot: Image to read data from.
426f996fc96SBojan Smojver  * @nr_to_write: Number of pages to save.
427f996fc96SBojan Smojver  */
428f996fc96SBojan Smojver static int save_image_lzo(struct swap_map_handle *handle,
429f996fc96SBojan Smojver                           struct snapshot_handle *snapshot,
430f996fc96SBojan Smojver                           unsigned int nr_to_write)
431f996fc96SBojan Smojver {
432f996fc96SBojan Smojver 	unsigned int m;
433f996fc96SBojan Smojver 	int ret = 0;
434f996fc96SBojan Smojver 	int nr_pages;
435f996fc96SBojan Smojver 	int err2;
436f996fc96SBojan Smojver 	struct bio *bio;
437f996fc96SBojan Smojver 	struct timeval start;
438f996fc96SBojan Smojver 	struct timeval stop;
439f996fc96SBojan Smojver 	size_t off, unc_len, cmp_len;
440f996fc96SBojan Smojver 	unsigned char *unc, *cmp, *wrk, *page;
441f996fc96SBojan Smojver 
442f996fc96SBojan Smojver 	page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
443f996fc96SBojan Smojver 	if (!page) {
444f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO page\n");
445f996fc96SBojan Smojver 		return -ENOMEM;
446f996fc96SBojan Smojver 	}
447f996fc96SBojan Smojver 
448f996fc96SBojan Smojver 	wrk = vmalloc(LZO1X_1_MEM_COMPRESS);
449f996fc96SBojan Smojver 	if (!wrk) {
450f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO workspace\n");
451f996fc96SBojan Smojver 		free_page((unsigned long)page);
452f996fc96SBojan Smojver 		return -ENOMEM;
453f996fc96SBojan Smojver 	}
454f996fc96SBojan Smojver 
455f996fc96SBojan Smojver 	unc = vmalloc(LZO_UNC_SIZE);
456f996fc96SBojan Smojver 	if (!unc) {
457f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n");
458f996fc96SBojan Smojver 		vfree(wrk);
459f996fc96SBojan Smojver 		free_page((unsigned long)page);
460f996fc96SBojan Smojver 		return -ENOMEM;
461f996fc96SBojan Smojver 	}
462f996fc96SBojan Smojver 
463f996fc96SBojan Smojver 	cmp = vmalloc(LZO_CMP_SIZE);
464f996fc96SBojan Smojver 	if (!cmp) {
465f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
466f996fc96SBojan Smojver 		vfree(unc);
467f996fc96SBojan Smojver 		vfree(wrk);
468f996fc96SBojan Smojver 		free_page((unsigned long)page);
469f996fc96SBojan Smojver 		return -ENOMEM;
470f996fc96SBojan Smojver 	}
471f996fc96SBojan Smojver 
472f996fc96SBojan Smojver 	printk(KERN_INFO
473f996fc96SBojan Smojver 		"PM: Compressing and saving image data (%u pages) ...     ",
474f996fc96SBojan Smojver 		nr_to_write);
475f996fc96SBojan Smojver 	m = nr_to_write / 100;
476f996fc96SBojan Smojver 	if (!m)
477f996fc96SBojan Smojver 		m = 1;
478f996fc96SBojan Smojver 	nr_pages = 0;
479f996fc96SBojan Smojver 	bio = NULL;
480f996fc96SBojan Smojver 	do_gettimeofday(&start);
481f996fc96SBojan Smojver 	for (;;) {
482f996fc96SBojan Smojver 		for (off = 0; off < LZO_UNC_SIZE; off += PAGE_SIZE) {
483f996fc96SBojan Smojver 			ret = snapshot_read_next(snapshot);
484f996fc96SBojan Smojver 			if (ret < 0)
485f996fc96SBojan Smojver 				goto out_finish;
486f996fc96SBojan Smojver 
487f996fc96SBojan Smojver 			if (!ret)
488f996fc96SBojan Smojver 				break;
489f996fc96SBojan Smojver 
490f996fc96SBojan Smojver 			memcpy(unc + off, data_of(*snapshot), PAGE_SIZE);
491f996fc96SBojan Smojver 
492f996fc96SBojan Smojver 			if (!(nr_pages % m))
493f996fc96SBojan Smojver 				printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m);
494f996fc96SBojan Smojver 			nr_pages++;
495f996fc96SBojan Smojver 		}
496f996fc96SBojan Smojver 
497f996fc96SBojan Smojver 		if (!off)
498f996fc96SBojan Smojver 			break;
499f996fc96SBojan Smojver 
500f996fc96SBojan Smojver 		unc_len = off;
501f996fc96SBojan Smojver 		ret = lzo1x_1_compress(unc, unc_len,
502f996fc96SBojan Smojver 		                       cmp + LZO_HEADER, &cmp_len, wrk);
503f996fc96SBojan Smojver 		if (ret < 0) {
504f996fc96SBojan Smojver 			printk(KERN_ERR "PM: LZO compression failed\n");
505f996fc96SBojan Smojver 			break;
506f996fc96SBojan Smojver 		}
507f996fc96SBojan Smojver 
508f996fc96SBojan Smojver 		if (unlikely(!cmp_len ||
509f996fc96SBojan Smojver 		             cmp_len > lzo1x_worst_compress(unc_len))) {
510f996fc96SBojan Smojver 			printk(KERN_ERR "PM: Invalid LZO compressed length\n");
511f996fc96SBojan Smojver 			ret = -1;
512f996fc96SBojan Smojver 			break;
513f996fc96SBojan Smojver 		}
514f996fc96SBojan Smojver 
515f996fc96SBojan Smojver 		*(size_t *)cmp = cmp_len;
516f996fc96SBojan Smojver 
517f996fc96SBojan Smojver 		/*
518f996fc96SBojan Smojver 		 * Given we are writing one page at a time to disk, we copy
519f996fc96SBojan Smojver 		 * that much from the buffer, although the last bit will likely
520f996fc96SBojan Smojver 		 * be smaller than full page. This is OK - we saved the length
521f996fc96SBojan Smojver 		 * of the compressed data, so any garbage at the end will be
522f996fc96SBojan Smojver 		 * discarded when we read it.
523f996fc96SBojan Smojver 		 */
524f996fc96SBojan Smojver 		for (off = 0; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) {
525f996fc96SBojan Smojver 			memcpy(page, cmp + off, PAGE_SIZE);
526f996fc96SBojan Smojver 
527f996fc96SBojan Smojver 			ret = swap_write_page(handle, page, &bio);
528f996fc96SBojan Smojver 			if (ret)
529f996fc96SBojan Smojver 				goto out_finish;
530f996fc96SBojan Smojver 		}
531f996fc96SBojan Smojver 	}
532f996fc96SBojan Smojver 
533f996fc96SBojan Smojver out_finish:
534f996fc96SBojan Smojver 	err2 = hib_wait_on_bio_chain(&bio);
535f996fc96SBojan Smojver 	do_gettimeofday(&stop);
536f996fc96SBojan Smojver 	if (!ret)
537f996fc96SBojan Smojver 		ret = err2;
538f996fc96SBojan Smojver 	if (!ret)
539f996fc96SBojan Smojver 		printk(KERN_CONT "\b\b\b\bdone\n");
540f996fc96SBojan Smojver 	else
541f996fc96SBojan Smojver 		printk(KERN_CONT "\n");
542f996fc96SBojan Smojver 	swsusp_show_speed(&start, &stop, nr_to_write, "Wrote");
543f996fc96SBojan Smojver 
544f996fc96SBojan Smojver 	vfree(cmp);
545f996fc96SBojan Smojver 	vfree(unc);
546f996fc96SBojan Smojver 	vfree(wrk);
547f996fc96SBojan Smojver 	free_page((unsigned long)page);
548f996fc96SBojan Smojver 
549f996fc96SBojan Smojver 	return ret;
550f996fc96SBojan Smojver }
551f996fc96SBojan Smojver 
55261159a31SRafael J. Wysocki /**
55361159a31SRafael J. Wysocki  *	enough_swap - Make sure we have enough swap to save the image.
55461159a31SRafael J. Wysocki  *
55561159a31SRafael J. Wysocki  *	Returns TRUE or FALSE after checking the total amount of swap
55661159a31SRafael J. Wysocki  *	space avaiable from the resume partition.
55761159a31SRafael J. Wysocki  */
55861159a31SRafael J. Wysocki 
559f996fc96SBojan Smojver static int enough_swap(unsigned int nr_pages, unsigned int flags)
56061159a31SRafael J. Wysocki {
56161159a31SRafael J. Wysocki 	unsigned int free_swap = count_swap_pages(root_swap, 1);
562f996fc96SBojan Smojver 	unsigned int required;
56361159a31SRafael J. Wysocki 
56423976728SRafael J. Wysocki 	pr_debug("PM: Free swap pages: %u\n", free_swap);
565f996fc96SBojan Smojver 
566f996fc96SBojan Smojver 	required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ?
567f996fc96SBojan Smojver 		nr_pages : (nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1);
568f996fc96SBojan Smojver 	return free_swap > required;
56961159a31SRafael J. Wysocki }
57061159a31SRafael J. Wysocki 
57161159a31SRafael J. Wysocki /**
57261159a31SRafael J. Wysocki  *	swsusp_write - Write entire image and metadata.
573a634cc10SRafael J. Wysocki  *	@flags: flags to pass to the "boot" kernel in the image header
57461159a31SRafael J. Wysocki  *
57561159a31SRafael J. Wysocki  *	It is important _NOT_ to umount filesystems at this point. We want
57661159a31SRafael J. Wysocki  *	them synced (in case something goes wrong) but we DO not want to mark
57761159a31SRafael J. Wysocki  *	filesystem clean: it is not. (And it does not matter, if we resume
57861159a31SRafael J. Wysocki  *	correctly, we'll mark system clean, anyway.)
57961159a31SRafael J. Wysocki  */
58061159a31SRafael J. Wysocki 
581a634cc10SRafael J. Wysocki int swsusp_write(unsigned int flags)
58261159a31SRafael J. Wysocki {
58361159a31SRafael J. Wysocki 	struct swap_map_handle handle;
58461159a31SRafael J. Wysocki 	struct snapshot_handle snapshot;
58561159a31SRafael J. Wysocki 	struct swsusp_info *header;
5866f612af5SJiri Slaby 	unsigned long pages;
58761159a31SRafael J. Wysocki 	int error;
58861159a31SRafael J. Wysocki 
5896f612af5SJiri Slaby 	pages = snapshot_get_image_size();
5906f612af5SJiri Slaby 	error = get_swap_writer(&handle);
5913aef83e0SRafael J. Wysocki 	if (error) {
5926f612af5SJiri Slaby 		printk(KERN_ERR "PM: Cannot get swap writer\n");
59361159a31SRafael J. Wysocki 		return error;
59461159a31SRafael J. Wysocki 	}
595f996fc96SBojan Smojver 	if (!enough_swap(pages, flags)) {
5966f612af5SJiri Slaby 		printk(KERN_ERR "PM: Not enough free swap\n");
5976f612af5SJiri Slaby 		error = -ENOSPC;
5986f612af5SJiri Slaby 		goto out_finish;
5996f612af5SJiri Slaby 	}
60061159a31SRafael J. Wysocki 	memset(&snapshot, 0, sizeof(struct snapshot_handle));
601d3c1b24cSJiri Slaby 	error = snapshot_read_next(&snapshot);
6023aef83e0SRafael J. Wysocki 	if (error < PAGE_SIZE) {
6033aef83e0SRafael J. Wysocki 		if (error >= 0)
6043aef83e0SRafael J. Wysocki 			error = -EFAULT;
6053aef83e0SRafael J. Wysocki 
6066f612af5SJiri Slaby 		goto out_finish;
6073aef83e0SRafael J. Wysocki 	}
60861159a31SRafael J. Wysocki 	header = (struct swsusp_info *)data_of(snapshot);
609ab954160SAndrew Morton 	error = swap_write_page(&handle, header, NULL);
610f996fc96SBojan Smojver 	if (!error) {
611f996fc96SBojan Smojver 		error = (flags & SF_NOCOMPRESS_MODE) ?
612f996fc96SBojan Smojver 			save_image(&handle, &snapshot, pages - 1) :
613f996fc96SBojan Smojver 			save_image_lzo(&handle, &snapshot, pages - 1);
614f996fc96SBojan Smojver 	}
6156f612af5SJiri Slaby out_finish:
6166f612af5SJiri Slaby 	error = swap_writer_finish(&handle, flags, error);
61761159a31SRafael J. Wysocki 	return error;
61861159a31SRafael J. Wysocki }
61961159a31SRafael J. Wysocki 
62061159a31SRafael J. Wysocki /**
62161159a31SRafael J. Wysocki  *	The following functions allow us to read data using a swap map
62261159a31SRafael J. Wysocki  *	in a file-alike way
62361159a31SRafael J. Wysocki  */
62461159a31SRafael J. Wysocki 
62561159a31SRafael J. Wysocki static void release_swap_reader(struct swap_map_handle *handle)
62661159a31SRafael J. Wysocki {
62761159a31SRafael J. Wysocki 	if (handle->cur)
62861159a31SRafael J. Wysocki 		free_page((unsigned long)handle->cur);
62961159a31SRafael J. Wysocki 	handle->cur = NULL;
63061159a31SRafael J. Wysocki }
63161159a31SRafael J. Wysocki 
6326f612af5SJiri Slaby static int get_swap_reader(struct swap_map_handle *handle,
6336f612af5SJiri Slaby 		unsigned int *flags_p)
63461159a31SRafael J. Wysocki {
63561159a31SRafael J. Wysocki 	int error;
63661159a31SRafael J. Wysocki 
6376f612af5SJiri Slaby 	*flags_p = swsusp_header->flags;
6386f612af5SJiri Slaby 
6396f612af5SJiri Slaby 	if (!swsusp_header->image) /* how can this happen? */
64061159a31SRafael J. Wysocki 		return -EINVAL;
6413aef83e0SRafael J. Wysocki 
64285949121SRafael J. Wysocki 	handle->cur = (struct swap_map_page *)get_zeroed_page(__GFP_WAIT | __GFP_HIGH);
64361159a31SRafael J. Wysocki 	if (!handle->cur)
64461159a31SRafael J. Wysocki 		return -ENOMEM;
6453aef83e0SRafael J. Wysocki 
6466f612af5SJiri Slaby 	error = hib_bio_read_page(swsusp_header->image, handle->cur, NULL);
64761159a31SRafael J. Wysocki 	if (error) {
64861159a31SRafael J. Wysocki 		release_swap_reader(handle);
64961159a31SRafael J. Wysocki 		return error;
65061159a31SRafael J. Wysocki 	}
65161159a31SRafael J. Wysocki 	handle->k = 0;
65261159a31SRafael J. Wysocki 	return 0;
65361159a31SRafael J. Wysocki }
65461159a31SRafael J. Wysocki 
655546e0d27SAndrew Morton static int swap_read_page(struct swap_map_handle *handle, void *buf,
656546e0d27SAndrew Morton 				struct bio **bio_chain)
65761159a31SRafael J. Wysocki {
6583aef83e0SRafael J. Wysocki 	sector_t offset;
65961159a31SRafael J. Wysocki 	int error;
66061159a31SRafael J. Wysocki 
66161159a31SRafael J. Wysocki 	if (!handle->cur)
66261159a31SRafael J. Wysocki 		return -EINVAL;
66361159a31SRafael J. Wysocki 	offset = handle->cur->entries[handle->k];
66461159a31SRafael J. Wysocki 	if (!offset)
66561159a31SRafael J. Wysocki 		return -EFAULT;
6668a0d613fSJiri Slaby 	error = hib_bio_read_page(offset, buf, bio_chain);
66761159a31SRafael J. Wysocki 	if (error)
66861159a31SRafael J. Wysocki 		return error;
66961159a31SRafael J. Wysocki 	if (++handle->k >= MAP_PAGE_ENTRIES) {
6708a0d613fSJiri Slaby 		error = hib_wait_on_bio_chain(bio_chain);
67161159a31SRafael J. Wysocki 		handle->k = 0;
67261159a31SRafael J. Wysocki 		offset = handle->cur->next_swap;
67361159a31SRafael J. Wysocki 		if (!offset)
67461159a31SRafael J. Wysocki 			release_swap_reader(handle);
675546e0d27SAndrew Morton 		else if (!error)
6768a0d613fSJiri Slaby 			error = hib_bio_read_page(offset, handle->cur, NULL);
67761159a31SRafael J. Wysocki 	}
67861159a31SRafael J. Wysocki 	return error;
67961159a31SRafael J. Wysocki }
68061159a31SRafael J. Wysocki 
6816f612af5SJiri Slaby static int swap_reader_finish(struct swap_map_handle *handle)
6826f612af5SJiri Slaby {
6836f612af5SJiri Slaby 	release_swap_reader(handle);
6846f612af5SJiri Slaby 
6856f612af5SJiri Slaby 	return 0;
6866f612af5SJiri Slaby }
6876f612af5SJiri Slaby 
68861159a31SRafael J. Wysocki /**
68961159a31SRafael J. Wysocki  *	load_image - load the image using the swap map handle
69061159a31SRafael J. Wysocki  *	@handle and the snapshot handle @snapshot
69161159a31SRafael J. Wysocki  *	(assume there are @nr_pages pages to load)
69261159a31SRafael J. Wysocki  */
69361159a31SRafael J. Wysocki 
69461159a31SRafael J. Wysocki static int load_image(struct swap_map_handle *handle,
69561159a31SRafael J. Wysocki                       struct snapshot_handle *snapshot,
696546e0d27SAndrew Morton                       unsigned int nr_to_read)
69761159a31SRafael J. Wysocki {
69861159a31SRafael J. Wysocki 	unsigned int m;
69961159a31SRafael J. Wysocki 	int error = 0;
7008c002494SAndrew Morton 	struct timeval start;
7018c002494SAndrew Morton 	struct timeval stop;
702546e0d27SAndrew Morton 	struct bio *bio;
703546e0d27SAndrew Morton 	int err2;
704546e0d27SAndrew Morton 	unsigned nr_pages;
70561159a31SRafael J. Wysocki 
70623976728SRafael J. Wysocki 	printk(KERN_INFO "PM: Loading image data pages (%u pages) ...     ",
70723976728SRafael J. Wysocki 		nr_to_read);
708546e0d27SAndrew Morton 	m = nr_to_read / 100;
70961159a31SRafael J. Wysocki 	if (!m)
71061159a31SRafael J. Wysocki 		m = 1;
71161159a31SRafael J. Wysocki 	nr_pages = 0;
712546e0d27SAndrew Morton 	bio = NULL;
7138c002494SAndrew Morton 	do_gettimeofday(&start);
714546e0d27SAndrew Morton 	for ( ; ; ) {
715d3c1b24cSJiri Slaby 		error = snapshot_write_next(snapshot);
716546e0d27SAndrew Morton 		if (error <= 0)
717546e0d27SAndrew Morton 			break;
718546e0d27SAndrew Morton 		error = swap_read_page(handle, data_of(*snapshot), &bio);
719546e0d27SAndrew Morton 		if (error)
720546e0d27SAndrew Morton 			break;
721546e0d27SAndrew Morton 		if (snapshot->sync_read)
7228a0d613fSJiri Slaby 			error = hib_wait_on_bio_chain(&bio);
72361159a31SRafael J. Wysocki 		if (error)
72461159a31SRafael J. Wysocki 			break;
72561159a31SRafael J. Wysocki 		if (!(nr_pages % m))
72661159a31SRafael J. Wysocki 			printk("\b\b\b\b%3d%%", nr_pages / m);
72761159a31SRafael J. Wysocki 		nr_pages++;
72861159a31SRafael J. Wysocki 	}
7298a0d613fSJiri Slaby 	err2 = hib_wait_on_bio_chain(&bio);
7308c002494SAndrew Morton 	do_gettimeofday(&stop);
731546e0d27SAndrew Morton 	if (!error)
732546e0d27SAndrew Morton 		error = err2;
733e655a250SCon Kolivas 	if (!error) {
73461159a31SRafael J. Wysocki 		printk("\b\b\b\bdone\n");
7358357376dSRafael J. Wysocki 		snapshot_write_finalize(snapshot);
73661159a31SRafael J. Wysocki 		if (!snapshot_image_loaded(snapshot))
73761159a31SRafael J. Wysocki 			error = -ENODATA;
738bf9fd67aSJiri Slaby 	} else
739bf9fd67aSJiri Slaby 		printk("\n");
7400d3a9abeSRafael J. Wysocki 	swsusp_show_speed(&start, &stop, nr_to_read, "Read");
74161159a31SRafael J. Wysocki 	return error;
74261159a31SRafael J. Wysocki }
74361159a31SRafael J. Wysocki 
744a634cc10SRafael J. Wysocki /**
745f996fc96SBojan Smojver  * load_image_lzo - Load compressed image data and decompress them with LZO.
746f996fc96SBojan Smojver  * @handle: Swap map handle to use for loading data.
747f996fc96SBojan Smojver  * @snapshot: Image to copy uncompressed data into.
748f996fc96SBojan Smojver  * @nr_to_read: Number of pages to load.
749f996fc96SBojan Smojver  */
750f996fc96SBojan Smojver static int load_image_lzo(struct swap_map_handle *handle,
751f996fc96SBojan Smojver                           struct snapshot_handle *snapshot,
752f996fc96SBojan Smojver                           unsigned int nr_to_read)
753f996fc96SBojan Smojver {
754f996fc96SBojan Smojver 	unsigned int m;
755f996fc96SBojan Smojver 	int error = 0;
756f996fc96SBojan Smojver 	struct timeval start;
757f996fc96SBojan Smojver 	struct timeval stop;
758f996fc96SBojan Smojver 	unsigned nr_pages;
759f996fc96SBojan Smojver 	size_t off, unc_len, cmp_len;
760f996fc96SBojan Smojver 	unsigned char *unc, *cmp, *page;
761f996fc96SBojan Smojver 
762f996fc96SBojan Smojver 	page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
763f996fc96SBojan Smojver 	if (!page) {
764f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO page\n");
765f996fc96SBojan Smojver 		return -ENOMEM;
766f996fc96SBojan Smojver 	}
767f996fc96SBojan Smojver 
768f996fc96SBojan Smojver 	unc = vmalloc(LZO_UNC_SIZE);
769f996fc96SBojan Smojver 	if (!unc) {
770f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n");
771f996fc96SBojan Smojver 		free_page((unsigned long)page);
772f996fc96SBojan Smojver 		return -ENOMEM;
773f996fc96SBojan Smojver 	}
774f996fc96SBojan Smojver 
775f996fc96SBojan Smojver 	cmp = vmalloc(LZO_CMP_SIZE);
776f996fc96SBojan Smojver 	if (!cmp) {
777f996fc96SBojan Smojver 		printk(KERN_ERR "PM: Failed to allocate LZO compressed\n");
778f996fc96SBojan Smojver 		vfree(unc);
779f996fc96SBojan Smojver 		free_page((unsigned long)page);
780f996fc96SBojan Smojver 		return -ENOMEM;
781f996fc96SBojan Smojver 	}
782f996fc96SBojan Smojver 
783f996fc96SBojan Smojver 	printk(KERN_INFO
784f996fc96SBojan Smojver 		"PM: Loading and decompressing image data (%u pages) ...     ",
785f996fc96SBojan Smojver 		nr_to_read);
786f996fc96SBojan Smojver 	m = nr_to_read / 100;
787f996fc96SBojan Smojver 	if (!m)
788f996fc96SBojan Smojver 		m = 1;
789f996fc96SBojan Smojver 	nr_pages = 0;
790f996fc96SBojan Smojver 	do_gettimeofday(&start);
791f996fc96SBojan Smojver 
792f996fc96SBojan Smojver 	error = snapshot_write_next(snapshot);
793f996fc96SBojan Smojver 	if (error <= 0)
794f996fc96SBojan Smojver 		goto out_finish;
795f996fc96SBojan Smojver 
796f996fc96SBojan Smojver 	for (;;) {
797f996fc96SBojan Smojver 		error = swap_read_page(handle, page, NULL); /* sync */
798f996fc96SBojan Smojver 		if (error)
799f996fc96SBojan Smojver 			break;
800f996fc96SBojan Smojver 
801f996fc96SBojan Smojver 		cmp_len = *(size_t *)page;
802f996fc96SBojan Smojver 		if (unlikely(!cmp_len ||
803f996fc96SBojan Smojver 		             cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) {
804f996fc96SBojan Smojver 			printk(KERN_ERR "PM: Invalid LZO compressed length\n");
805f996fc96SBojan Smojver 			error = -1;
806f996fc96SBojan Smojver 			break;
807f996fc96SBojan Smojver 		}
808f996fc96SBojan Smojver 
809f996fc96SBojan Smojver 		memcpy(cmp, page, PAGE_SIZE);
810f996fc96SBojan Smojver 		for (off = PAGE_SIZE; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) {
811f996fc96SBojan Smojver 			error = swap_read_page(handle, page, NULL); /* sync */
812f996fc96SBojan Smojver 			if (error)
813f996fc96SBojan Smojver 				goto out_finish;
814f996fc96SBojan Smojver 
815f996fc96SBojan Smojver 			memcpy(cmp + off, page, PAGE_SIZE);
816f996fc96SBojan Smojver 		}
817f996fc96SBojan Smojver 
818f996fc96SBojan Smojver 		unc_len = LZO_UNC_SIZE;
819f996fc96SBojan Smojver 		error = lzo1x_decompress_safe(cmp + LZO_HEADER, cmp_len,
820f996fc96SBojan Smojver 		                              unc, &unc_len);
821f996fc96SBojan Smojver 		if (error < 0) {
822f996fc96SBojan Smojver 			printk(KERN_ERR "PM: LZO decompression failed\n");
823f996fc96SBojan Smojver 			break;
824f996fc96SBojan Smojver 		}
825f996fc96SBojan Smojver 
826f996fc96SBojan Smojver 		if (unlikely(!unc_len ||
827f996fc96SBojan Smojver 		             unc_len > LZO_UNC_SIZE ||
828f996fc96SBojan Smojver 		             unc_len & (PAGE_SIZE - 1))) {
829f996fc96SBojan Smojver 			printk(KERN_ERR "PM: Invalid LZO uncompressed length\n");
830f996fc96SBojan Smojver 			error = -1;
831f996fc96SBojan Smojver 			break;
832f996fc96SBojan Smojver 		}
833f996fc96SBojan Smojver 
834f996fc96SBojan Smojver 		for (off = 0; off < unc_len; off += PAGE_SIZE) {
835f996fc96SBojan Smojver 			memcpy(data_of(*snapshot), unc + off, PAGE_SIZE);
836f996fc96SBojan Smojver 
837f996fc96SBojan Smojver 			if (!(nr_pages % m))
838f996fc96SBojan Smojver 				printk("\b\b\b\b%3d%%", nr_pages / m);
839f996fc96SBojan Smojver 			nr_pages++;
840f996fc96SBojan Smojver 
841f996fc96SBojan Smojver 			error = snapshot_write_next(snapshot);
842f996fc96SBojan Smojver 			if (error <= 0)
843f996fc96SBojan Smojver 				goto out_finish;
844f996fc96SBojan Smojver 		}
845f996fc96SBojan Smojver 	}
846f996fc96SBojan Smojver 
847f996fc96SBojan Smojver out_finish:
848f996fc96SBojan Smojver 	do_gettimeofday(&stop);
849f996fc96SBojan Smojver 	if (!error) {
850f996fc96SBojan Smojver 		printk("\b\b\b\bdone\n");
851f996fc96SBojan Smojver 		snapshot_write_finalize(snapshot);
852f996fc96SBojan Smojver 		if (!snapshot_image_loaded(snapshot))
853f996fc96SBojan Smojver 			error = -ENODATA;
854f996fc96SBojan Smojver 	} else
855f996fc96SBojan Smojver 		printk("\n");
856f996fc96SBojan Smojver 	swsusp_show_speed(&start, &stop, nr_to_read, "Read");
857f996fc96SBojan Smojver 
858f996fc96SBojan Smojver 	vfree(cmp);
859f996fc96SBojan Smojver 	vfree(unc);
860f996fc96SBojan Smojver 	free_page((unsigned long)page);
861f996fc96SBojan Smojver 
862f996fc96SBojan Smojver 	return error;
863f996fc96SBojan Smojver }
864f996fc96SBojan Smojver 
865f996fc96SBojan Smojver /**
866a634cc10SRafael J. Wysocki  *	swsusp_read - read the hibernation image.
867a634cc10SRafael J. Wysocki  *	@flags_p: flags passed by the "frozen" kernel in the image header should
868b595076aSUwe Kleine-König  *		  be written into this memory location
869a634cc10SRafael J. Wysocki  */
870a634cc10SRafael J. Wysocki 
871a634cc10SRafael J. Wysocki int swsusp_read(unsigned int *flags_p)
87261159a31SRafael J. Wysocki {
87361159a31SRafael J. Wysocki 	int error;
87461159a31SRafael J. Wysocki 	struct swap_map_handle handle;
87561159a31SRafael J. Wysocki 	struct snapshot_handle snapshot;
87661159a31SRafael J. Wysocki 	struct swsusp_info *header;
87761159a31SRafael J. Wysocki 
87861159a31SRafael J. Wysocki 	memset(&snapshot, 0, sizeof(struct snapshot_handle));
879d3c1b24cSJiri Slaby 	error = snapshot_write_next(&snapshot);
88061159a31SRafael J. Wysocki 	if (error < PAGE_SIZE)
88161159a31SRafael J. Wysocki 		return error < 0 ? error : -EFAULT;
88261159a31SRafael J. Wysocki 	header = (struct swsusp_info *)data_of(snapshot);
8836f612af5SJiri Slaby 	error = get_swap_reader(&handle, flags_p);
8846f612af5SJiri Slaby 	if (error)
8856f612af5SJiri Slaby 		goto end;
88661159a31SRafael J. Wysocki 	if (!error)
887546e0d27SAndrew Morton 		error = swap_read_page(&handle, header, NULL);
888f996fc96SBojan Smojver 	if (!error) {
889f996fc96SBojan Smojver 		error = (*flags_p & SF_NOCOMPRESS_MODE) ?
890f996fc96SBojan Smojver 			load_image(&handle, &snapshot, header->pages - 1) :
891f996fc96SBojan Smojver 			load_image_lzo(&handle, &snapshot, header->pages - 1);
892f996fc96SBojan Smojver 	}
8936f612af5SJiri Slaby 	swap_reader_finish(&handle);
8946f612af5SJiri Slaby end:
89561159a31SRafael J. Wysocki 	if (!error)
89623976728SRafael J. Wysocki 		pr_debug("PM: Image successfully loaded\n");
89761159a31SRafael J. Wysocki 	else
89823976728SRafael J. Wysocki 		pr_debug("PM: Error %d resuming\n", error);
89961159a31SRafael J. Wysocki 	return error;
90061159a31SRafael J. Wysocki }
90161159a31SRafael J. Wysocki 
90261159a31SRafael J. Wysocki /**
90361159a31SRafael J. Wysocki  *      swsusp_check - Check for swsusp signature in the resume device
90461159a31SRafael J. Wysocki  */
90561159a31SRafael J. Wysocki 
90661159a31SRafael J. Wysocki int swsusp_check(void)
90761159a31SRafael J. Wysocki {
90861159a31SRafael J. Wysocki 	int error;
90961159a31SRafael J. Wysocki 
9108a0d613fSJiri Slaby 	hib_resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
9118a0d613fSJiri Slaby 	if (!IS_ERR(hib_resume_bdev)) {
9128a0d613fSJiri Slaby 		set_blocksize(hib_resume_bdev, PAGE_SIZE);
9133ecb01dfSJan Beulich 		clear_page(swsusp_header);
9148a0d613fSJiri Slaby 		error = hib_bio_read_page(swsusp_resume_block,
9151b29c164SVivek Goyal 					swsusp_header, NULL);
9169a154d9dSRafael J. Wysocki 		if (error)
91776b57e61SJiri Slaby 			goto put;
9189a154d9dSRafael J. Wysocki 
9193624eb04SRafael J. Wysocki 		if (!memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) {
9201b29c164SVivek Goyal 			memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10);
92161159a31SRafael J. Wysocki 			/* Reset swap signature now */
9228a0d613fSJiri Slaby 			error = hib_bio_write_page(swsusp_resume_block,
9231b29c164SVivek Goyal 						swsusp_header, NULL);
92461159a31SRafael J. Wysocki 		} else {
92576b57e61SJiri Slaby 			error = -EINVAL;
92661159a31SRafael J. Wysocki 		}
92776b57e61SJiri Slaby 
92876b57e61SJiri Slaby put:
92961159a31SRafael J. Wysocki 		if (error)
9308a0d613fSJiri Slaby 			blkdev_put(hib_resume_bdev, FMODE_READ);
93161159a31SRafael J. Wysocki 		else
932d0941eadSRafael J. Wysocki 			pr_debug("PM: Image signature found, resuming\n");
93361159a31SRafael J. Wysocki 	} else {
9348a0d613fSJiri Slaby 		error = PTR_ERR(hib_resume_bdev);
93561159a31SRafael J. Wysocki 	}
93661159a31SRafael J. Wysocki 
93761159a31SRafael J. Wysocki 	if (error)
938d0941eadSRafael J. Wysocki 		pr_debug("PM: Image not found (code %d)\n", error);
93961159a31SRafael J. Wysocki 
94061159a31SRafael J. Wysocki 	return error;
94161159a31SRafael J. Wysocki }
94261159a31SRafael J. Wysocki 
94361159a31SRafael J. Wysocki /**
94461159a31SRafael J. Wysocki  *	swsusp_close - close swap device.
94561159a31SRafael J. Wysocki  */
94661159a31SRafael J. Wysocki 
947c2dd0daeSAl Viro void swsusp_close(fmode_t mode)
94861159a31SRafael J. Wysocki {
9498a0d613fSJiri Slaby 	if (IS_ERR(hib_resume_bdev)) {
95023976728SRafael J. Wysocki 		pr_debug("PM: Image device not initialised\n");
95161159a31SRafael J. Wysocki 		return;
95261159a31SRafael J. Wysocki 	}
95361159a31SRafael J. Wysocki 
9548a0d613fSJiri Slaby 	blkdev_put(hib_resume_bdev, mode);
95561159a31SRafael J. Wysocki }
9561b29c164SVivek Goyal 
9571b29c164SVivek Goyal static int swsusp_header_init(void)
9581b29c164SVivek Goyal {
9591b29c164SVivek Goyal 	swsusp_header = (struct swsusp_header*) __get_free_page(GFP_KERNEL);
9601b29c164SVivek Goyal 	if (!swsusp_header)
9611b29c164SVivek Goyal 		panic("Could not allocate memory for swsusp_header\n");
9621b29c164SVivek Goyal 	return 0;
9631b29c164SVivek Goyal }
9641b29c164SVivek Goyal 
9651b29c164SVivek Goyal core_initcall(swsusp_header_init);
966