xref: /openbmc/linux/kernel/power/swap.c (revision 9a154d9d)
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  *
761159a31SRafael J. Wysocki  * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.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/smp_lock.h>
1661159a31SRafael J. Wysocki #include <linux/file.h>
1761159a31SRafael J. Wysocki #include <linux/utsname.h>
1861159a31SRafael J. Wysocki #include <linux/version.h>
1961159a31SRafael J. Wysocki #include <linux/delay.h>
2061159a31SRafael J. Wysocki #include <linux/bitops.h>
2161159a31SRafael J. Wysocki #include <linux/genhd.h>
2261159a31SRafael J. Wysocki #include <linux/device.h>
2361159a31SRafael J. Wysocki #include <linux/buffer_head.h>
2461159a31SRafael J. Wysocki #include <linux/bio.h>
25546e0d27SAndrew Morton #include <linux/blkdev.h>
2661159a31SRafael J. Wysocki #include <linux/swap.h>
2761159a31SRafael J. Wysocki #include <linux/swapops.h>
2861159a31SRafael J. Wysocki #include <linux/pm.h>
2961159a31SRafael J. Wysocki 
3061159a31SRafael J. Wysocki #include "power.h"
3161159a31SRafael J. Wysocki 
3261159a31SRafael J. Wysocki extern char resume_file[];
3361159a31SRafael J. Wysocki 
3461159a31SRafael J. Wysocki #define SWSUSP_SIG	"S1SUSPEND"
3561159a31SRafael J. Wysocki 
3661159a31SRafael J. Wysocki static struct swsusp_header {
373aef83e0SRafael J. Wysocki 	char reserved[PAGE_SIZE - 20 - sizeof(sector_t)];
383aef83e0SRafael J. Wysocki 	sector_t image;
3961159a31SRafael J. Wysocki 	char	orig_sig[10];
4061159a31SRafael J. Wysocki 	char	sig[10];
4161159a31SRafael J. Wysocki } __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
4261159a31SRafael J. Wysocki 
4361159a31SRafael J. Wysocki /*
443fc6b34fSRafael J. Wysocki  * General things
4561159a31SRafael J. Wysocki  */
4661159a31SRafael J. Wysocki 
4761159a31SRafael J. Wysocki static unsigned short root_swap = 0xffff;
483fc6b34fSRafael J. Wysocki static struct block_device *resume_bdev;
493fc6b34fSRafael J. Wysocki 
503fc6b34fSRafael J. Wysocki /**
513fc6b34fSRafael J. Wysocki  *	submit - submit BIO request.
523fc6b34fSRafael J. Wysocki  *	@rw:	READ or WRITE.
533fc6b34fSRafael J. Wysocki  *	@off	physical offset of page.
543fc6b34fSRafael J. Wysocki  *	@page:	page we're reading or writing.
553fc6b34fSRafael J. Wysocki  *	@bio_chain: list of pending biod (for async reading)
563fc6b34fSRafael J. Wysocki  *
573fc6b34fSRafael J. Wysocki  *	Straight from the textbook - allocate and initialize the bio.
583fc6b34fSRafael J. Wysocki  *	If we're reading, make sure the page is marked as dirty.
593fc6b34fSRafael J. Wysocki  *	Then submit it and, if @bio_chain == NULL, wait.
603fc6b34fSRafael J. Wysocki  */
613fc6b34fSRafael J. Wysocki static int submit(int rw, pgoff_t page_off, struct page *page,
623fc6b34fSRafael J. Wysocki 			struct bio **bio_chain)
633fc6b34fSRafael J. Wysocki {
643fc6b34fSRafael J. Wysocki 	struct bio *bio;
653fc6b34fSRafael J. Wysocki 
663fc6b34fSRafael J. Wysocki 	bio = bio_alloc(GFP_ATOMIC, 1);
673fc6b34fSRafael J. Wysocki 	if (!bio)
683fc6b34fSRafael J. Wysocki 		return -ENOMEM;
693fc6b34fSRafael J. Wysocki 	bio->bi_sector = page_off * (PAGE_SIZE >> 9);
703fc6b34fSRafael J. Wysocki 	bio->bi_bdev = resume_bdev;
713fc6b34fSRafael J. Wysocki 	bio->bi_end_io = end_swap_bio_read;
723fc6b34fSRafael J. Wysocki 
733fc6b34fSRafael J. Wysocki 	if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
743fc6b34fSRafael J. Wysocki 		printk("swsusp: ERROR: adding page to bio at %ld\n", page_off);
753fc6b34fSRafael J. Wysocki 		bio_put(bio);
763fc6b34fSRafael J. Wysocki 		return -EFAULT;
773fc6b34fSRafael J. Wysocki 	}
783fc6b34fSRafael J. Wysocki 
793fc6b34fSRafael J. Wysocki 	lock_page(page);
803fc6b34fSRafael J. Wysocki 	bio_get(bio);
813fc6b34fSRafael J. Wysocki 
823fc6b34fSRafael J. Wysocki 	if (bio_chain == NULL) {
833fc6b34fSRafael J. Wysocki 		submit_bio(rw | (1 << BIO_RW_SYNC), bio);
843fc6b34fSRafael J. Wysocki 		wait_on_page_locked(page);
853fc6b34fSRafael J. Wysocki 		if (rw == READ)
863fc6b34fSRafael J. Wysocki 			bio_set_pages_dirty(bio);
873fc6b34fSRafael J. Wysocki 		bio_put(bio);
883fc6b34fSRafael J. Wysocki 	} else {
893fc6b34fSRafael J. Wysocki 		if (rw == READ)
903fc6b34fSRafael J. Wysocki 			get_page(page);	/* These pages are freed later */
913fc6b34fSRafael J. Wysocki 		bio->bi_private = *bio_chain;
923fc6b34fSRafael J. Wysocki 		*bio_chain = bio;
933fc6b34fSRafael J. Wysocki 		submit_bio(rw | (1 << BIO_RW_SYNC), bio);
943fc6b34fSRafael J. Wysocki 	}
953fc6b34fSRafael J. Wysocki 	return 0;
963fc6b34fSRafael J. Wysocki }
973fc6b34fSRafael J. Wysocki 
983fc6b34fSRafael J. Wysocki static int bio_read_page(pgoff_t page_off, void *addr, struct bio **bio_chain)
993fc6b34fSRafael J. Wysocki {
1003fc6b34fSRafael J. Wysocki 	return submit(READ, page_off, virt_to_page(addr), bio_chain);
1013fc6b34fSRafael J. Wysocki }
1023fc6b34fSRafael J. Wysocki 
1033aef83e0SRafael J. Wysocki static int bio_write_page(pgoff_t page_off, void *addr, struct bio **bio_chain)
1043fc6b34fSRafael J. Wysocki {
1053aef83e0SRafael J. Wysocki 	return submit(WRITE, page_off, virt_to_page(addr), bio_chain);
1063fc6b34fSRafael J. Wysocki }
1073fc6b34fSRafael J. Wysocki 
1083fc6b34fSRafael J. Wysocki static int wait_on_bio_chain(struct bio **bio_chain)
1093fc6b34fSRafael J. Wysocki {
1103fc6b34fSRafael J. Wysocki 	struct bio *bio;
1113fc6b34fSRafael J. Wysocki 	struct bio *next_bio;
1123fc6b34fSRafael J. Wysocki 	int ret = 0;
1133fc6b34fSRafael J. Wysocki 
1143fc6b34fSRafael J. Wysocki 	if (bio_chain == NULL)
1153fc6b34fSRafael J. Wysocki 		return 0;
1163fc6b34fSRafael J. Wysocki 
1173fc6b34fSRafael J. Wysocki 	bio = *bio_chain;
1183fc6b34fSRafael J. Wysocki 	if (bio == NULL)
1193fc6b34fSRafael J. Wysocki 		return 0;
1203fc6b34fSRafael J. Wysocki 	while (bio) {
1213fc6b34fSRafael J. Wysocki 		struct page *page;
1223fc6b34fSRafael J. Wysocki 
1233fc6b34fSRafael J. Wysocki 		next_bio = bio->bi_private;
1243fc6b34fSRafael J. Wysocki 		page = bio->bi_io_vec[0].bv_page;
1253fc6b34fSRafael J. Wysocki 		wait_on_page_locked(page);
1263fc6b34fSRafael J. Wysocki 		if (!PageUptodate(page) || PageError(page))
1273fc6b34fSRafael J. Wysocki 			ret = -EIO;
1283fc6b34fSRafael J. Wysocki 		put_page(page);
1293fc6b34fSRafael J. Wysocki 		bio_put(bio);
1303fc6b34fSRafael J. Wysocki 		bio = next_bio;
1313fc6b34fSRafael J. Wysocki 	}
1323fc6b34fSRafael J. Wysocki 	*bio_chain = NULL;
1333fc6b34fSRafael J. Wysocki 	return ret;
1343fc6b34fSRafael J. Wysocki }
1353fc6b34fSRafael J. Wysocki 
1363fc6b34fSRafael J. Wysocki static void show_speed(struct timeval *start, struct timeval *stop,
1373fc6b34fSRafael J. Wysocki 			unsigned nr_pages, char *msg)
1383fc6b34fSRafael J. Wysocki {
1393fc6b34fSRafael J. Wysocki 	s64 elapsed_centisecs64;
1403fc6b34fSRafael J. Wysocki 	int centisecs;
1413fc6b34fSRafael J. Wysocki 	int k;
1423fc6b34fSRafael J. Wysocki 	int kps;
1433fc6b34fSRafael J. Wysocki 
1443fc6b34fSRafael J. Wysocki 	elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start);
1453fc6b34fSRafael J. Wysocki 	do_div(elapsed_centisecs64, NSEC_PER_SEC / 100);
1463fc6b34fSRafael J. Wysocki 	centisecs = elapsed_centisecs64;
1473fc6b34fSRafael J. Wysocki 	if (centisecs == 0)
1483fc6b34fSRafael J. Wysocki 		centisecs = 1;	/* avoid div-by-zero */
1493fc6b34fSRafael J. Wysocki 	k = nr_pages * (PAGE_SIZE / 1024);
1503fc6b34fSRafael J. Wysocki 	kps = (k * 100) / centisecs;
1513fc6b34fSRafael J. Wysocki 	printk("%s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", msg, k,
1523fc6b34fSRafael J. Wysocki 			centisecs / 100, centisecs % 100,
1533fc6b34fSRafael J. Wysocki 			kps / 1000, (kps % 1000) / 10);
1543fc6b34fSRafael J. Wysocki }
1553fc6b34fSRafael J. Wysocki 
1563fc6b34fSRafael J. Wysocki /*
1573fc6b34fSRafael J. Wysocki  * Saving part
1583fc6b34fSRafael J. Wysocki  */
15961159a31SRafael J. Wysocki 
1603aef83e0SRafael J. Wysocki static int mark_swapfiles(sector_t start)
16161159a31SRafael J. Wysocki {
16261159a31SRafael J. Wysocki 	int error;
16361159a31SRafael J. Wysocki 
1649a154d9dSRafael J. Wysocki 	bio_read_page(swsusp_resume_block, &swsusp_header, NULL);
16561159a31SRafael J. Wysocki 	if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) ||
16661159a31SRafael J. Wysocki 	    !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
16761159a31SRafael J. Wysocki 		memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
16861159a31SRafael J. Wysocki 		memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
16961159a31SRafael J. Wysocki 		swsusp_header.image = start;
1709a154d9dSRafael J. Wysocki 		error = bio_write_page(swsusp_resume_block,
1719a154d9dSRafael J. Wysocki 					&swsusp_header, NULL);
17261159a31SRafael J. Wysocki 	} else {
1733aef83e0SRafael J. Wysocki 		printk(KERN_ERR "swsusp: Swap header not found!\n");
17461159a31SRafael J. Wysocki 		error = -ENODEV;
17561159a31SRafael J. Wysocki 	}
17661159a31SRafael J. Wysocki 	return error;
17761159a31SRafael J. Wysocki }
17861159a31SRafael J. Wysocki 
17961159a31SRafael J. Wysocki /**
18061159a31SRafael J. Wysocki  *	swsusp_swap_check - check if the resume device is a swap device
18161159a31SRafael J. Wysocki  *	and get its index (if so)
18261159a31SRafael J. Wysocki  */
18361159a31SRafael J. Wysocki 
18461159a31SRafael J. Wysocki static int swsusp_swap_check(void) /* This is called before saving image */
18561159a31SRafael J. Wysocki {
1863aef83e0SRafael J. Wysocki 	int res;
18761159a31SRafael J. Wysocki 
1889a154d9dSRafael J. Wysocki 	res = swap_type_of(swsusp_resume_device, swsusp_resume_block);
1893aef83e0SRafael J. Wysocki 	if (res < 0)
1903aef83e0SRafael J. Wysocki 		return res;
1913aef83e0SRafael J. Wysocki 
19261159a31SRafael J. Wysocki 	root_swap = res;
1933aef83e0SRafael J. Wysocki 	resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_WRITE);
1943aef83e0SRafael J. Wysocki 	if (IS_ERR(resume_bdev))
1953aef83e0SRafael J. Wysocki 		return PTR_ERR(resume_bdev);
1963aef83e0SRafael J. Wysocki 
1973aef83e0SRafael J. Wysocki 	res = set_blocksize(resume_bdev, PAGE_SIZE);
1983aef83e0SRafael J. Wysocki 	if (res < 0)
1993aef83e0SRafael J. Wysocki 		blkdev_put(resume_bdev);
2003aef83e0SRafael J. Wysocki 
20161159a31SRafael J. Wysocki 	return res;
20261159a31SRafael J. Wysocki }
20361159a31SRafael J. Wysocki 
20461159a31SRafael J. Wysocki /**
20561159a31SRafael J. Wysocki  *	write_page - Write one page to given swap location.
20661159a31SRafael J. Wysocki  *	@buf:		Address we're writing.
20761159a31SRafael J. Wysocki  *	@offset:	Offset of the swap page we're writing to.
208ab954160SAndrew Morton  *	@bio_chain:	Link the next write BIO here
20961159a31SRafael J. Wysocki  */
21061159a31SRafael J. Wysocki 
2113aef83e0SRafael J. Wysocki static int write_page(void *buf, sector_t offset, struct bio **bio_chain)
21261159a31SRafael J. Wysocki {
2133aef83e0SRafael J. Wysocki 	void *src;
21461159a31SRafael J. Wysocki 
2153aef83e0SRafael J. Wysocki 	if (!offset)
2163aef83e0SRafael J. Wysocki 		return -ENOSPC;
217ab954160SAndrew Morton 
218ab954160SAndrew Morton 	if (bio_chain) {
2193aef83e0SRafael J. Wysocki 		src = (void *)__get_free_page(GFP_ATOMIC);
2203aef83e0SRafael J. Wysocki 		if (src) {
2213aef83e0SRafael J. Wysocki 			memcpy(src, buf, PAGE_SIZE);
2223aef83e0SRafael J. Wysocki 		} else {
223ab954160SAndrew Morton 			WARN_ON_ONCE(1);
224ab954160SAndrew Morton 			bio_chain = NULL;	/* Go synchronous */
2253aef83e0SRafael J. Wysocki 			src = buf;
2263aef83e0SRafael J. Wysocki 		}
227ab954160SAndrew Morton 	} else {
2283aef83e0SRafael J. Wysocki 		src = buf;
229ab954160SAndrew Morton 	}
2303aef83e0SRafael J. Wysocki 	return bio_write_page(offset, src, bio_chain);
23161159a31SRafael J. Wysocki }
23261159a31SRafael J. Wysocki 
23361159a31SRafael J. Wysocki /*
23461159a31SRafael J. Wysocki  *	The swap map is a data structure used for keeping track of each page
23561159a31SRafael J. Wysocki  *	written to a swap partition.  It consists of many swap_map_page
23661159a31SRafael J. Wysocki  *	structures that contain each an array of MAP_PAGE_SIZE swap entries.
23761159a31SRafael J. Wysocki  *	These structures are stored on the swap and linked together with the
23861159a31SRafael J. Wysocki  *	help of the .next_swap member.
23961159a31SRafael J. Wysocki  *
24061159a31SRafael J. Wysocki  *	The swap map is created during suspend.  The swap map pages are
24161159a31SRafael J. Wysocki  *	allocated and populated one at a time, so we only need one memory
24261159a31SRafael J. Wysocki  *	page to set up the entire structure.
24361159a31SRafael J. Wysocki  *
24461159a31SRafael J. Wysocki  *	During resume we also only need to use one swap_map_page structure
24561159a31SRafael J. Wysocki  *	at a time.
24661159a31SRafael J. Wysocki  */
24761159a31SRafael J. Wysocki 
2483aef83e0SRafael J. Wysocki #define MAP_PAGE_ENTRIES	(PAGE_SIZE / sizeof(sector_t) - 1)
24961159a31SRafael J. Wysocki 
25061159a31SRafael J. Wysocki struct swap_map_page {
2513aef83e0SRafael J. Wysocki 	sector_t entries[MAP_PAGE_ENTRIES];
2523aef83e0SRafael J. Wysocki 	sector_t next_swap;
25361159a31SRafael J. Wysocki };
25461159a31SRafael J. Wysocki 
25561159a31SRafael J. Wysocki /**
25661159a31SRafael J. Wysocki  *	The swap_map_handle structure is used for handling swap in
25761159a31SRafael J. Wysocki  *	a file-alike way
25861159a31SRafael J. Wysocki  */
25961159a31SRafael J. Wysocki 
26061159a31SRafael J. Wysocki struct swap_map_handle {
26161159a31SRafael J. Wysocki 	struct swap_map_page *cur;
2623aef83e0SRafael J. Wysocki 	sector_t cur_swap;
26361159a31SRafael J. Wysocki 	struct bitmap_page *bitmap;
26461159a31SRafael J. Wysocki 	unsigned int k;
26561159a31SRafael J. Wysocki };
26661159a31SRafael J. Wysocki 
26761159a31SRafael J. Wysocki static void release_swap_writer(struct swap_map_handle *handle)
26861159a31SRafael J. Wysocki {
26961159a31SRafael J. Wysocki 	if (handle->cur)
27061159a31SRafael J. Wysocki 		free_page((unsigned long)handle->cur);
27161159a31SRafael J. Wysocki 	handle->cur = NULL;
27261159a31SRafael J. Wysocki 	if (handle->bitmap)
27361159a31SRafael J. Wysocki 		free_bitmap(handle->bitmap);
27461159a31SRafael J. Wysocki 	handle->bitmap = NULL;
27561159a31SRafael J. Wysocki }
27661159a31SRafael J. Wysocki 
27761159a31SRafael J. Wysocki static int get_swap_writer(struct swap_map_handle *handle)
27861159a31SRafael J. Wysocki {
27961159a31SRafael J. Wysocki 	handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
28061159a31SRafael J. Wysocki 	if (!handle->cur)
28161159a31SRafael J. Wysocki 		return -ENOMEM;
28261159a31SRafael J. Wysocki 	handle->bitmap = alloc_bitmap(count_swap_pages(root_swap, 0));
28361159a31SRafael J. Wysocki 	if (!handle->bitmap) {
28461159a31SRafael J. Wysocki 		release_swap_writer(handle);
28561159a31SRafael J. Wysocki 		return -ENOMEM;
28661159a31SRafael J. Wysocki 	}
2873aef83e0SRafael J. Wysocki 	handle->cur_swap = alloc_swapdev_block(root_swap, handle->bitmap);
28861159a31SRafael J. Wysocki 	if (!handle->cur_swap) {
28961159a31SRafael J. Wysocki 		release_swap_writer(handle);
29061159a31SRafael J. Wysocki 		return -ENOSPC;
29161159a31SRafael J. Wysocki 	}
29261159a31SRafael J. Wysocki 	handle->k = 0;
29361159a31SRafael J. Wysocki 	return 0;
29461159a31SRafael J. Wysocki }
29561159a31SRafael J. Wysocki 
296ab954160SAndrew Morton static int swap_write_page(struct swap_map_handle *handle, void *buf,
297ab954160SAndrew Morton 				struct bio **bio_chain)
298ab954160SAndrew Morton {
299ab954160SAndrew Morton 	int error = 0;
3003aef83e0SRafael J. Wysocki 	sector_t offset;
30161159a31SRafael J. Wysocki 
30261159a31SRafael J. Wysocki 	if (!handle->cur)
30361159a31SRafael J. Wysocki 		return -EINVAL;
3043aef83e0SRafael J. Wysocki 	offset = alloc_swapdev_block(root_swap, handle->bitmap);
305ab954160SAndrew Morton 	error = write_page(buf, offset, bio_chain);
30661159a31SRafael J. Wysocki 	if (error)
30761159a31SRafael J. Wysocki 		return error;
30861159a31SRafael J. Wysocki 	handle->cur->entries[handle->k++] = offset;
30961159a31SRafael J. Wysocki 	if (handle->k >= MAP_PAGE_ENTRIES) {
310ab954160SAndrew Morton 		error = wait_on_bio_chain(bio_chain);
311ab954160SAndrew Morton 		if (error)
312ab954160SAndrew Morton 			goto out;
3133aef83e0SRafael J. Wysocki 		offset = alloc_swapdev_block(root_swap, handle->bitmap);
31461159a31SRafael J. Wysocki 		if (!offset)
31561159a31SRafael J. Wysocki 			return -ENOSPC;
31661159a31SRafael J. Wysocki 		handle->cur->next_swap = offset;
317ab954160SAndrew Morton 		error = write_page(handle->cur, handle->cur_swap, NULL);
31861159a31SRafael J. Wysocki 		if (error)
319ab954160SAndrew Morton 			goto out;
32061159a31SRafael J. Wysocki 		memset(handle->cur, 0, PAGE_SIZE);
32161159a31SRafael J. Wysocki 		handle->cur_swap = offset;
32261159a31SRafael J. Wysocki 		handle->k = 0;
32361159a31SRafael J. Wysocki 	}
324ab954160SAndrew Morton out:
325ab954160SAndrew Morton 	return error;
32661159a31SRafael J. Wysocki }
32761159a31SRafael J. Wysocki 
32861159a31SRafael J. Wysocki static int flush_swap_writer(struct swap_map_handle *handle)
32961159a31SRafael J. Wysocki {
33061159a31SRafael J. Wysocki 	if (handle->cur && handle->cur_swap)
331ab954160SAndrew Morton 		return write_page(handle->cur, handle->cur_swap, NULL);
33261159a31SRafael J. Wysocki 	else
33361159a31SRafael J. Wysocki 		return -EINVAL;
33461159a31SRafael J. Wysocki }
33561159a31SRafael J. Wysocki 
33661159a31SRafael J. Wysocki /**
33761159a31SRafael J. Wysocki  *	save_image - save the suspend image data
33861159a31SRafael J. Wysocki  */
33961159a31SRafael J. Wysocki 
34061159a31SRafael J. Wysocki static int save_image(struct swap_map_handle *handle,
34161159a31SRafael J. Wysocki                       struct snapshot_handle *snapshot,
3423a4f7577SAndrew Morton                       unsigned int nr_to_write)
34361159a31SRafael J. Wysocki {
34461159a31SRafael J. Wysocki 	unsigned int m;
34561159a31SRafael J. Wysocki 	int ret;
34661159a31SRafael J. Wysocki 	int error = 0;
3473a4f7577SAndrew Morton 	int nr_pages;
348ab954160SAndrew Morton 	int err2;
349ab954160SAndrew Morton 	struct bio *bio;
3503a4f7577SAndrew Morton 	struct timeval start;
3513a4f7577SAndrew Morton 	struct timeval stop;
35261159a31SRafael J. Wysocki 
3533a4f7577SAndrew Morton 	printk("Saving image data pages (%u pages) ...     ", nr_to_write);
3543a4f7577SAndrew Morton 	m = nr_to_write / 100;
35561159a31SRafael J. Wysocki 	if (!m)
35661159a31SRafael J. Wysocki 		m = 1;
35761159a31SRafael J. Wysocki 	nr_pages = 0;
358ab954160SAndrew Morton 	bio = NULL;
3593a4f7577SAndrew Morton 	do_gettimeofday(&start);
36061159a31SRafael J. Wysocki 	do {
36161159a31SRafael J. Wysocki 		ret = snapshot_read_next(snapshot, PAGE_SIZE);
36261159a31SRafael J. Wysocki 		if (ret > 0) {
363ab954160SAndrew Morton 			error = swap_write_page(handle, data_of(*snapshot),
364ab954160SAndrew Morton 						&bio);
36561159a31SRafael J. Wysocki 			if (error)
36661159a31SRafael J. Wysocki 				break;
36761159a31SRafael J. Wysocki 			if (!(nr_pages % m))
36861159a31SRafael J. Wysocki 				printk("\b\b\b\b%3d%%", nr_pages / m);
36961159a31SRafael J. Wysocki 			nr_pages++;
37061159a31SRafael J. Wysocki 		}
37161159a31SRafael J. Wysocki 	} while (ret > 0);
372ab954160SAndrew Morton 	err2 = wait_on_bio_chain(&bio);
3733a4f7577SAndrew Morton 	do_gettimeofday(&stop);
37461159a31SRafael J. Wysocki 	if (!error)
375ab954160SAndrew Morton 		error = err2;
376ab954160SAndrew Morton 	if (!error)
37761159a31SRafael J. Wysocki 		printk("\b\b\b\bdone\n");
3783a4f7577SAndrew Morton 	show_speed(&start, &stop, nr_to_write, "Wrote");
37961159a31SRafael J. Wysocki 	return error;
38061159a31SRafael J. Wysocki }
38161159a31SRafael J. Wysocki 
38261159a31SRafael J. Wysocki /**
38361159a31SRafael J. Wysocki  *	enough_swap - Make sure we have enough swap to save the image.
38461159a31SRafael J. Wysocki  *
38561159a31SRafael J. Wysocki  *	Returns TRUE or FALSE after checking the total amount of swap
38661159a31SRafael J. Wysocki  *	space avaiable from the resume partition.
38761159a31SRafael J. Wysocki  */
38861159a31SRafael J. Wysocki 
38961159a31SRafael J. Wysocki static int enough_swap(unsigned int nr_pages)
39061159a31SRafael J. Wysocki {
39161159a31SRafael J. Wysocki 	unsigned int free_swap = count_swap_pages(root_swap, 1);
39261159a31SRafael J. Wysocki 
39361159a31SRafael J. Wysocki 	pr_debug("swsusp: free swap pages: %u\n", free_swap);
394940864ddSRafael J. Wysocki 	return free_swap > nr_pages + PAGES_FOR_IO;
39561159a31SRafael J. Wysocki }
39661159a31SRafael J. Wysocki 
39761159a31SRafael J. Wysocki /**
39861159a31SRafael J. Wysocki  *	swsusp_write - Write entire image and metadata.
39961159a31SRafael J. Wysocki  *
40061159a31SRafael J. Wysocki  *	It is important _NOT_ to umount filesystems at this point. We want
40161159a31SRafael J. Wysocki  *	them synced (in case something goes wrong) but we DO not want to mark
40261159a31SRafael J. Wysocki  *	filesystem clean: it is not. (And it does not matter, if we resume
40361159a31SRafael J. Wysocki  *	correctly, we'll mark system clean, anyway.)
40461159a31SRafael J. Wysocki  */
40561159a31SRafael J. Wysocki 
40661159a31SRafael J. Wysocki int swsusp_write(void)
40761159a31SRafael J. Wysocki {
40861159a31SRafael J. Wysocki 	struct swap_map_handle handle;
40961159a31SRafael J. Wysocki 	struct snapshot_handle snapshot;
41061159a31SRafael J. Wysocki 	struct swsusp_info *header;
41161159a31SRafael J. Wysocki 	int error;
41261159a31SRafael J. Wysocki 
4133aef83e0SRafael J. Wysocki 	error = swsusp_swap_check();
4143aef83e0SRafael J. Wysocki 	if (error) {
415546e0d27SAndrew Morton 		printk(KERN_ERR "swsusp: Cannot find swap device, try "
416546e0d27SAndrew Morton 				"swapon -a.\n");
41761159a31SRafael J. Wysocki 		return error;
41861159a31SRafael J. Wysocki 	}
41961159a31SRafael J. Wysocki 	memset(&snapshot, 0, sizeof(struct snapshot_handle));
42061159a31SRafael J. Wysocki 	error = snapshot_read_next(&snapshot, PAGE_SIZE);
4213aef83e0SRafael J. Wysocki 	if (error < PAGE_SIZE) {
4223aef83e0SRafael J. Wysocki 		if (error >= 0)
4233aef83e0SRafael J. Wysocki 			error = -EFAULT;
4243aef83e0SRafael J. Wysocki 
4253aef83e0SRafael J. Wysocki 		goto out;
4263aef83e0SRafael J. Wysocki 	}
42761159a31SRafael J. Wysocki 	header = (struct swsusp_info *)data_of(snapshot);
42861159a31SRafael J. Wysocki 	if (!enough_swap(header->pages)) {
42961159a31SRafael J. Wysocki 		printk(KERN_ERR "swsusp: Not enough free swap\n");
4303aef83e0SRafael J. Wysocki 		error = -ENOSPC;
4313aef83e0SRafael J. Wysocki 		goto out;
43261159a31SRafael J. Wysocki 	}
43361159a31SRafael J. Wysocki 	error = get_swap_writer(&handle);
43461159a31SRafael J. Wysocki 	if (!error) {
4353aef83e0SRafael J. Wysocki 		sector_t start = handle.cur_swap;
4363aef83e0SRafael J. Wysocki 
437ab954160SAndrew Morton 		error = swap_write_page(&handle, header, NULL);
43861159a31SRafael J. Wysocki 		if (!error)
439712f403aSAndrew Morton 			error = save_image(&handle, &snapshot,
440712f403aSAndrew Morton 					header->pages - 1);
4413aef83e0SRafael J. Wysocki 
44261159a31SRafael J. Wysocki 		if (!error) {
44361159a31SRafael J. Wysocki 			flush_swap_writer(&handle);
44461159a31SRafael J. Wysocki 			printk("S");
4453aef83e0SRafael J. Wysocki 			error = mark_swapfiles(start);
44661159a31SRafael J. Wysocki 			printk("|\n");
44761159a31SRafael J. Wysocki 		}
448712f403aSAndrew Morton 	}
44961159a31SRafael J. Wysocki 	if (error)
45061159a31SRafael J. Wysocki 		free_all_swap_pages(root_swap, handle.bitmap);
45161159a31SRafael J. Wysocki 	release_swap_writer(&handle);
4523aef83e0SRafael J. Wysocki out:
4533aef83e0SRafael J. Wysocki 	swsusp_close();
45461159a31SRafael J. Wysocki 	return error;
45561159a31SRafael J. Wysocki }
45661159a31SRafael J. Wysocki 
45761159a31SRafael J. Wysocki /**
45861159a31SRafael J. Wysocki  *	The following functions allow us to read data using a swap map
45961159a31SRafael J. Wysocki  *	in a file-alike way
46061159a31SRafael J. Wysocki  */
46161159a31SRafael J. Wysocki 
46261159a31SRafael J. Wysocki static void release_swap_reader(struct swap_map_handle *handle)
46361159a31SRafael J. Wysocki {
46461159a31SRafael J. Wysocki 	if (handle->cur)
46561159a31SRafael J. Wysocki 		free_page((unsigned long)handle->cur);
46661159a31SRafael J. Wysocki 	handle->cur = NULL;
46761159a31SRafael J. Wysocki }
46861159a31SRafael J. Wysocki 
4693aef83e0SRafael J. Wysocki static int get_swap_reader(struct swap_map_handle *handle, sector_t start)
47061159a31SRafael J. Wysocki {
47161159a31SRafael J. Wysocki 	int error;
47261159a31SRafael J. Wysocki 
4733aef83e0SRafael J. Wysocki 	if (!start)
47461159a31SRafael J. Wysocki 		return -EINVAL;
4753aef83e0SRafael J. Wysocki 
47661159a31SRafael J. Wysocki 	handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
47761159a31SRafael J. Wysocki 	if (!handle->cur)
47861159a31SRafael J. Wysocki 		return -ENOMEM;
4793aef83e0SRafael J. Wysocki 
4803aef83e0SRafael J. Wysocki 	error = bio_read_page(start, handle->cur, NULL);
48161159a31SRafael J. Wysocki 	if (error) {
48261159a31SRafael J. Wysocki 		release_swap_reader(handle);
48361159a31SRafael J. Wysocki 		return error;
48461159a31SRafael J. Wysocki 	}
48561159a31SRafael J. Wysocki 	handle->k = 0;
48661159a31SRafael J. Wysocki 	return 0;
48761159a31SRafael J. Wysocki }
48861159a31SRafael J. Wysocki 
489546e0d27SAndrew Morton static int swap_read_page(struct swap_map_handle *handle, void *buf,
490546e0d27SAndrew Morton 				struct bio **bio_chain)
49161159a31SRafael J. Wysocki {
4923aef83e0SRafael J. Wysocki 	sector_t offset;
49361159a31SRafael J. Wysocki 	int error;
49461159a31SRafael J. Wysocki 
49561159a31SRafael J. Wysocki 	if (!handle->cur)
49661159a31SRafael J. Wysocki 		return -EINVAL;
49761159a31SRafael J. Wysocki 	offset = handle->cur->entries[handle->k];
49861159a31SRafael J. Wysocki 	if (!offset)
49961159a31SRafael J. Wysocki 		return -EFAULT;
500546e0d27SAndrew Morton 	error = bio_read_page(offset, buf, bio_chain);
50161159a31SRafael J. Wysocki 	if (error)
50261159a31SRafael J. Wysocki 		return error;
50361159a31SRafael J. Wysocki 	if (++handle->k >= MAP_PAGE_ENTRIES) {
504546e0d27SAndrew Morton 		error = wait_on_bio_chain(bio_chain);
50561159a31SRafael J. Wysocki 		handle->k = 0;
50661159a31SRafael J. Wysocki 		offset = handle->cur->next_swap;
50761159a31SRafael J. Wysocki 		if (!offset)
50861159a31SRafael J. Wysocki 			release_swap_reader(handle);
509546e0d27SAndrew Morton 		else if (!error)
510546e0d27SAndrew Morton 			error = bio_read_page(offset, handle->cur, NULL);
51161159a31SRafael J. Wysocki 	}
51261159a31SRafael J. Wysocki 	return error;
51361159a31SRafael J. Wysocki }
51461159a31SRafael J. Wysocki 
51561159a31SRafael J. Wysocki /**
51661159a31SRafael J. Wysocki  *	load_image - load the image using the swap map handle
51761159a31SRafael J. Wysocki  *	@handle and the snapshot handle @snapshot
51861159a31SRafael J. Wysocki  *	(assume there are @nr_pages pages to load)
51961159a31SRafael J. Wysocki  */
52061159a31SRafael J. Wysocki 
52161159a31SRafael J. Wysocki static int load_image(struct swap_map_handle *handle,
52261159a31SRafael J. Wysocki                       struct snapshot_handle *snapshot,
523546e0d27SAndrew Morton                       unsigned int nr_to_read)
52461159a31SRafael J. Wysocki {
52561159a31SRafael J. Wysocki 	unsigned int m;
52661159a31SRafael J. Wysocki 	int error = 0;
5278c002494SAndrew Morton 	struct timeval start;
5288c002494SAndrew Morton 	struct timeval stop;
529546e0d27SAndrew Morton 	struct bio *bio;
530546e0d27SAndrew Morton 	int err2;
531546e0d27SAndrew Morton 	unsigned nr_pages;
53261159a31SRafael J. Wysocki 
533546e0d27SAndrew Morton 	printk("Loading image data pages (%u pages) ...     ", nr_to_read);
534546e0d27SAndrew Morton 	m = nr_to_read / 100;
53561159a31SRafael J. Wysocki 	if (!m)
53661159a31SRafael J. Wysocki 		m = 1;
53761159a31SRafael J. Wysocki 	nr_pages = 0;
538546e0d27SAndrew Morton 	bio = NULL;
5398c002494SAndrew Morton 	do_gettimeofday(&start);
540546e0d27SAndrew Morton 	for ( ; ; ) {
541546e0d27SAndrew Morton 		error = snapshot_write_next(snapshot, PAGE_SIZE);
542546e0d27SAndrew Morton 		if (error <= 0)
543546e0d27SAndrew Morton 			break;
544546e0d27SAndrew Morton 		error = swap_read_page(handle, data_of(*snapshot), &bio);
545546e0d27SAndrew Morton 		if (error)
546546e0d27SAndrew Morton 			break;
547546e0d27SAndrew Morton 		if (snapshot->sync_read)
548546e0d27SAndrew Morton 			error = wait_on_bio_chain(&bio);
54961159a31SRafael J. Wysocki 		if (error)
55061159a31SRafael J. Wysocki 			break;
55161159a31SRafael J. Wysocki 		if (!(nr_pages % m))
55261159a31SRafael J. Wysocki 			printk("\b\b\b\b%3d%%", nr_pages / m);
55361159a31SRafael J. Wysocki 		nr_pages++;
55461159a31SRafael J. Wysocki 	}
555546e0d27SAndrew Morton 	err2 = wait_on_bio_chain(&bio);
5568c002494SAndrew Morton 	do_gettimeofday(&stop);
557546e0d27SAndrew Morton 	if (!error)
558546e0d27SAndrew Morton 		error = err2;
559e655a250SCon Kolivas 	if (!error) {
56061159a31SRafael J. Wysocki 		printk("\b\b\b\bdone\n");
561940864ddSRafael J. Wysocki 		snapshot_free_unused_memory(snapshot);
56261159a31SRafael J. Wysocki 		if (!snapshot_image_loaded(snapshot))
56361159a31SRafael J. Wysocki 			error = -ENODATA;
564e655a250SCon Kolivas 	}
565546e0d27SAndrew Morton 	show_speed(&start, &stop, nr_to_read, "Read");
56661159a31SRafael J. Wysocki 	return error;
56761159a31SRafael J. Wysocki }
56861159a31SRafael J. Wysocki 
56961159a31SRafael J. Wysocki int swsusp_read(void)
57061159a31SRafael J. Wysocki {
57161159a31SRafael J. Wysocki 	int error;
57261159a31SRafael J. Wysocki 	struct swap_map_handle handle;
57361159a31SRafael J. Wysocki 	struct snapshot_handle snapshot;
57461159a31SRafael J. Wysocki 	struct swsusp_info *header;
57561159a31SRafael J. Wysocki 
57661159a31SRafael J. Wysocki 	if (IS_ERR(resume_bdev)) {
57761159a31SRafael J. Wysocki 		pr_debug("swsusp: block device not initialised\n");
57861159a31SRafael J. Wysocki 		return PTR_ERR(resume_bdev);
57961159a31SRafael J. Wysocki 	}
58061159a31SRafael J. Wysocki 
58161159a31SRafael J. Wysocki 	memset(&snapshot, 0, sizeof(struct snapshot_handle));
58261159a31SRafael J. Wysocki 	error = snapshot_write_next(&snapshot, PAGE_SIZE);
58361159a31SRafael J. Wysocki 	if (error < PAGE_SIZE)
58461159a31SRafael J. Wysocki 		return error < 0 ? error : -EFAULT;
58561159a31SRafael J. Wysocki 	header = (struct swsusp_info *)data_of(snapshot);
58661159a31SRafael J. Wysocki 	error = get_swap_reader(&handle, swsusp_header.image);
58761159a31SRafael J. Wysocki 	if (!error)
588546e0d27SAndrew Morton 		error = swap_read_page(&handle, header, NULL);
58961159a31SRafael J. Wysocki 	if (!error)
59061159a31SRafael J. Wysocki 		error = load_image(&handle, &snapshot, header->pages - 1);
59161159a31SRafael J. Wysocki 	release_swap_reader(&handle);
59261159a31SRafael J. Wysocki 
59361159a31SRafael J. Wysocki 	blkdev_put(resume_bdev);
59461159a31SRafael J. Wysocki 
59561159a31SRafael J. Wysocki 	if (!error)
59661159a31SRafael J. Wysocki 		pr_debug("swsusp: Reading resume file was successful\n");
59761159a31SRafael J. Wysocki 	else
59861159a31SRafael J. Wysocki 		pr_debug("swsusp: Error %d resuming\n", error);
59961159a31SRafael J. Wysocki 	return error;
60061159a31SRafael J. Wysocki }
60161159a31SRafael J. Wysocki 
60261159a31SRafael J. Wysocki /**
60361159a31SRafael J. Wysocki  *      swsusp_check - Check for swsusp signature in the resume device
60461159a31SRafael J. Wysocki  */
60561159a31SRafael J. Wysocki 
60661159a31SRafael J. Wysocki int swsusp_check(void)
60761159a31SRafael J. Wysocki {
60861159a31SRafael J. Wysocki 	int error;
60961159a31SRafael J. Wysocki 
61061159a31SRafael J. Wysocki 	resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
61161159a31SRafael J. Wysocki 	if (!IS_ERR(resume_bdev)) {
61261159a31SRafael J. Wysocki 		set_blocksize(resume_bdev, PAGE_SIZE);
61361159a31SRafael J. Wysocki 		memset(&swsusp_header, 0, sizeof(swsusp_header));
6149a154d9dSRafael J. Wysocki 		error = bio_read_page(swsusp_resume_block,
6159a154d9dSRafael J. Wysocki 					&swsusp_header, NULL);
6169a154d9dSRafael J. Wysocki 		if (error)
61761159a31SRafael J. Wysocki 			return error;
6189a154d9dSRafael J. Wysocki 
61961159a31SRafael J. Wysocki 		if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
62061159a31SRafael J. Wysocki 			memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
62161159a31SRafael J. Wysocki 			/* Reset swap signature now */
6229a154d9dSRafael J. Wysocki 			error = bio_write_page(swsusp_resume_block,
6239a154d9dSRafael J. Wysocki 						&swsusp_header, NULL);
62461159a31SRafael J. Wysocki 		} else {
62561159a31SRafael J. Wysocki 			return -EINVAL;
62661159a31SRafael J. Wysocki 		}
62761159a31SRafael J. Wysocki 		if (error)
62861159a31SRafael J. Wysocki 			blkdev_put(resume_bdev);
62961159a31SRafael J. Wysocki 		else
63061159a31SRafael J. Wysocki 			pr_debug("swsusp: Signature found, resuming\n");
63161159a31SRafael J. Wysocki 	} else {
63261159a31SRafael J. Wysocki 		error = PTR_ERR(resume_bdev);
63361159a31SRafael J. Wysocki 	}
63461159a31SRafael J. Wysocki 
63561159a31SRafael J. Wysocki 	if (error)
63661159a31SRafael J. Wysocki 		pr_debug("swsusp: Error %d check for resume file\n", error);
63761159a31SRafael J. Wysocki 
63861159a31SRafael J. Wysocki 	return error;
63961159a31SRafael J. Wysocki }
64061159a31SRafael J. Wysocki 
64161159a31SRafael J. Wysocki /**
64261159a31SRafael J. Wysocki  *	swsusp_close - close swap device.
64361159a31SRafael J. Wysocki  */
64461159a31SRafael J. Wysocki 
64561159a31SRafael J. Wysocki void swsusp_close(void)
64661159a31SRafael J. Wysocki {
64761159a31SRafael J. Wysocki 	if (IS_ERR(resume_bdev)) {
64861159a31SRafael J. Wysocki 		pr_debug("swsusp: block device not initialised\n");
64961159a31SRafael J. Wysocki 		return;
65061159a31SRafael J. Wysocki 	}
65161159a31SRafael J. Wysocki 
65261159a31SRafael J. Wysocki 	blkdev_put(resume_bdev);
65361159a31SRafael J. Wysocki }
654