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