* [RFC/RFT][PATCH -mm 0/4] swsusp: userland interface
@ 2005-12-28 0:08 Rafael J. Wysocki
2005-12-28 0:17 ` [RFC/RFT][PATCH -mm 1/4] swsusp: low level interface Rafael J. Wysocki
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2005-12-28 0:08 UTC (permalink / raw)
To: Linux PM; +Cc: LKML, Pavel Machek
[-- Attachment #1: Type: text/plain, Size: 2153 bytes --]
Hi,
This most probably needs some more work, but I think it's sufficient for
a preview.
The following series of patches creates a user space interface for swsusp
that makes it possible to move the image-writing and reading functionality
of swsusp to the user space.
The interface is based on a special character device allowing a user space
process to initiate suspend or resume using ioctls and to write or read the
system memory snapshot from the device (actually more operations are
defined on the device). The device itself is introduced by the second patch.
The first patch adds some low-level snapshot-handling functions. The
overall idea is that the in-kernel swap-handling code of swsusp
and the userland interface code should use the same set of low-level functions
operating on the system memory snapshot.
The last two patches are auxiliary. The third one moves the in-kernel
swap-writing and reading code to a separate file, called swap.c, and the last
patch moves the highmem-handling code to swsusp.c.
The patches apply on top of the three swsusp-related patches I posted
earlier today. In case you'd like to run some tests, the entire series
applicable on top of 2.6.15-rc5-mm3 is available at:
http://www.sisk.pl/kernel/patches/2.6.15-rc5-mm3/
(the other patches in the series are either bugfixes or debug patches,
and the order is given by the "series" file).
Also available are very simple demo userland utilities using the interface:
http://www.sisk.pl/kernel/utilities/suspend/
that are also attached to this message. The "suspend" utility is designed
to work from the (root) shell, but the "resume" utility should be
invoked from an initrd (I just point /linuxrc to it and start the kernel
with the "noresume" command line parameter to prevent the in-kernel
resume code from reading the image). The utilities are intentionally 100%
compatible with the in-kernel swap-writing and reading code (ie. the
image created by "suspend" can be restored by the in-kernel code etc.).
Any feedback will be very much appreciated.
Greetings,
Rafael
--
Beer is proof that God loves us and wants us to be happy - Benjamin Franklin
[-- Attachment #2: swsusp.h --]
[-- Type: text/x-chdr, Size: 4293 bytes --]
/*
* swsusp.h
*
* Common definitions for user space suspend and resume handlers.
*
* Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
*
* This file is released under the GPLv2.
*
*/
#define PAGE_SIZE 4096
#define SNAPSHOT_IOC_MAGIC '3'
#define SNAPSHOT_IOCFREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
#define SNAPSHOT_IOCUNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
#define SNAPSHOT_IOCATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
#define SNAPSHOT_IOCATOMIC_RESTORE _IO(SNAPSHOT_IOC_MAGIC, 4)
#define SNAPSHOT_IOCSET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 5, unsigned long)
#define SNAPSHOT_IOCAVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 6, void *)
#define SNAPSHOT_IOCGET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
#define SNAPSHOT_IOCFREE_SWAP_PAGE _IOW(SNAPSHOT_IOC_MAGIC, 8, unsigned long)
#define SNAPSHOT_IOC_MAXNR 8
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793
#define LINUX_REBOOT_CMD_RESTART 0x01234567
#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
struct new_utsname {
char sysname[65];
char nodename[65];
char release[65];
char version[65];
char machine[65];
char domainname[65];
};
struct swsusp_info {
struct new_utsname uts;
unsigned int version_code;
unsigned long num_physpages;
int cpus;
unsigned long image_pages;
unsigned long pages;
} __attribute__((aligned(PAGE_SIZE)));
#define SWSUSP_SIG "S1SUSPEND"
static struct swsusp_header {
char reserved[PAGE_SIZE - 20 - sizeof(long)];
unsigned long image;
char orig_sig[10];
char sig[10];
} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
static inline int freeze(int dev)
{
return ioctl(dev, SNAPSHOT_IOCFREEZE, 0);
}
static inline int unfreeze(int dev)
{
return ioctl(dev, SNAPSHOT_IOCUNFREEZE, 0);
}
static inline int atomic_snapshot(int dev, int *in_suspend)
{
return ioctl(dev, SNAPSHOT_IOCATOMIC_SNAPSHOT, in_suspend);
}
static inline int atomic_restore(int dev)
{
return ioctl(dev, SNAPSHOT_IOCATOMIC_RESTORE, 0);
}
static inline int set_image_size(int dev, unsigned int size)
{
return ioctl(dev, SNAPSHOT_IOCSET_IMAGE_SIZE, size);
}
static inline void reboot(void)
{
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART, 0);
}
static inline void power_off(void)
{
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF, 0);
}
/*
* The swap map is a data structure used for keeping track of each page
* written to the swap. It consists of many swap_map_page structures
* that contain each an array of MAP_PAGE_SIZE swap entries.
* These structures are linked together with the help of either the
* .next (in memory) or the .next_swap (in swap) member.
*
* The swap map is created during suspend. At that time we need to keep
* it in memory, because we have to free all of the allocated swap
* entries if an error occurs. The memory needed is preallocated
* so that we know in advance if there's enough of it.
*
* The first swap_map_page structure is filled with the swap entries that
* correspond to the first MAP_PAGE_SIZE data pages written to swap and
* so on. After the all of the data pages have been written, the order
* of the swap_map_page structures in the map is reversed so that they
* can be read from swap in the original order. This causes the data
* pages to be loaded in exactly the same order in which they have been
* saved.
*
* During resume we only need to use one swap_map_page structure
* at a time, which means that we only need to use two memory pages for
* reading the image - one for reading the swap_map_page structures
* and the second for reading the data pages from swap.
*/
#define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(long) - sizeof(void *)) \
/ sizeof(long))
struct swap_map_page {
unsigned long entries[MAP_PAGE_SIZE];
unsigned long next_swap;
struct swap_map_page *next;
};
/*
* The swap_map_handle structure is used for handling the swap map in
* a file-alike way
*/
struct swap_map_handle {
struct swap_map_page *cur;
unsigned int k;
int fd;
};
#define SNAPSHOT_DEVICE "/dev/snapshot"
#define RESUME_DEVICE "/dev/hdc3"
#define DEFAULT_IMAGE_SIZE 500
[-- Attachment #3: suspend.c --]
[-- Type: text/x-csrc, Size: 8364 bytes --]
/*
* suspend.c
*
* A simple user space suspend handler for swsusp.
*
* Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
*
* This file is released under the GPLv2.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <syscall.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "swsusp.h"
static int dev;
static char buffer[PAGE_SIZE];
static inline unsigned int check_free_swap(void)
{
int error;
unsigned int free_swap;
error = ioctl(dev, SNAPSHOT_IOCAVAIL_SWAP, &free_swap);
if (!error)
return free_swap;
else
printf("Error: errno = %d\n", errno);
return 0;
}
static inline unsigned long get_swap_page(void)
{
int error;
unsigned long offset;
error = ioctl(dev, SNAPSHOT_IOCGET_SWAP_PAGE, &offset);
if (!error)
return offset;
return 0;
}
static inline int free_swap_page(unsigned long offset)
{
return ioctl(dev, SNAPSHOT_IOCFREE_SWAP_PAGE, offset);
}
/**
* write_page - Write one page to a fresh swap location.
* @fd: File handle of the resume partition
* @buf: Pointer to the area we're writing.
* @offp: Place to store the swap offset we used.
*/
static int write_page(int fd, void *buf, unsigned long *offp)
{
unsigned long swap_offset;
off_t offset;
int res = -ENOSPC;
ssize_t cnt = 0;
swap_offset = get_swap_page();
if (swap_offset) {
offset = swap_offset * PAGE_SIZE;
if (lseek(fd, offset, SEEK_SET) == offset)
cnt = write(fd, buf, PAGE_SIZE);
if (cnt == PAGE_SIZE) {
*offp = swap_offset;
res = 0;
} else {
free_swap_page(swap_offset);
if (cnt < 0)
res = cnt;
}
}
return res;
}
static inline void free_swap_map(struct swap_map_page *swap_map)
{
struct swap_map_page *swp;
while (swap_map) {
swp = swap_map->next;
free(swap_map);
swap_map = swp;
}
}
static struct swap_map_page *alloc_swap_map(unsigned int nr_pages)
{
struct swap_map_page *swap_map, *swp;
unsigned n = 0;
if (!nr_pages)
return NULL;
printf("alloc_swap_map(): nr_pages = %d\n", nr_pages);
swap_map = malloc(PAGE_SIZE);
memset(swap_map, 0, PAGE_SIZE);
swp = swap_map;
for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) {
swp->next = malloc(PAGE_SIZE);
memset(swp->next, 0, PAGE_SIZE);
swp = swp->next;
if (!swp) {
free_swap_map(swap_map);
return NULL;
}
}
return swap_map;
}
/**
* reverse_swap_map - reverse the order of pages in the swap map
* @swap_map
*/
static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map)
{
struct swap_map_page *prev, *next;
prev = NULL;
while (swap_map) {
next = swap_map->next;
swap_map->next = prev;
prev = swap_map;
swap_map = next;
}
return prev;
}
/**
* free_swap_map_entries - free the swap entries allocated to store
* the swap map @swap_map (this is only called in case of an error)
*/
static inline void free_swap_map_entries(struct swap_map_page *swap_map)
{
while (swap_map) {
if (swap_map->next_swap)
free_swap_page(swap_map->next_swap);
swap_map = swap_map->next;
}
}
/**
* save_swap_map - save the swap map used for tracing the data pages
* stored in the swap
*/
static int save_swap_map(int fd, struct swap_map_page *swap_map, unsigned long *start)
{
unsigned long offset = 0;
int error;
while (swap_map) {
swap_map->next_swap = offset;
if ((error = write_page(fd, swap_map, &offset)))
return error;
swap_map = swap_map->next;
}
*start = offset;
return 0;
}
/**
* free_image_entries - free the swap entries allocated to store
* the image data pages (this is only called in case of an error)
*/
static inline void free_image_entries(struct swap_map_page *swp)
{
unsigned k;
while (swp) {
for (k = 0; k < MAP_PAGE_SIZE; k++)
if (swp->entries[k])
free_swap_page(swp->entries[k]);
swp = swp->next;
}
}
static inline void init_swap_map_handle(struct swap_map_handle *handle,
int fd, struct swap_map_page *map)
{
handle->cur = map;
handle->k = 0;
handle->fd = fd;
}
static inline int swap_map_write_page(struct swap_map_handle *handle, void *buf)
{
int error;
error = write_page(handle->fd, buf, handle->cur->entries + handle->k);
if (error)
return error;
if (++handle->k >= MAP_PAGE_SIZE) {
handle->cur = handle->cur->next;
handle->k = 0;
}
return 0;
}
/**
* save_image - save the suspend image data
*/
static int save_image(struct swap_map_handle *handle,
unsigned int nr_pages)
{
unsigned int m;
int ret;
int error = 0;
printf("suspend: Saving image data pages (%u pages) ... ", nr_pages);
m = nr_pages / 100;
if (!m)
m = 1;
nr_pages = 0;
do {
ret = read(dev, buffer, PAGE_SIZE);
if (ret > 0) {
error = swap_map_write_page(handle, buffer);
if (error)
break;
if (!(nr_pages % m))
printf("\b\b\b\b%3d%%", nr_pages / m);
nr_pages++;
}
} while (ret > 0);
if (!error)
printf(" done\n");
return error;
}
/**
* enough_swap - Make sure we have enough swap to save the image.
*
* Returns TRUE or FALSE after checking the total amount of swap
* space avaiable from the resume partition.
*/
static int enough_swap(unsigned int nr_pages)
{
unsigned int free_swap = check_free_swap();
printf("suspend: Free swap pages: %u\n", free_swap);
return free_swap > nr_pages;
}
static int mark_swapfiles(int fd, unsigned long start)
{
int error = 0;
lseek(fd, 0, SEEK_SET);
if (read(fd, &swsusp_header, PAGE_SIZE) < PAGE_SIZE)
return -EIO;
if (!memcmp("SWAP-SPACE", swsusp_header.sig, 10) ||
!memcmp("SWAPSPACE2", swsusp_header.sig, 10)) {
memcpy(swsusp_header.orig_sig, swsusp_header.sig, 10);
memcpy(swsusp_header.sig, SWSUSP_SIG, 10);
swsusp_header.image = start;
lseek(fd, 0, SEEK_SET);
if (write(fd, &swsusp_header, PAGE_SIZE) < PAGE_SIZE)
error = -EIO;
} else {
printf("suspend: Device is not a swap space.\n");
error = -ENODEV;
}
return error;
}
/**
* write_image - Write entire image and metadata.
*/
int write_image(char *resume_dev_name)
{
struct swap_map_page *swap_map;
struct swap_map_handle handle;
struct swsusp_info *header;
unsigned long start;
int fd;
int error;
fd = open(resume_dev_name, O_RDWR | O_SYNC);
if (fd < 0) {
printf("suspend: Could not open resume device\n");
return error;
}
error = read(dev, buffer, PAGE_SIZE);
if (error < PAGE_SIZE)
return error < 0 ? error : -EFAULT;
header = (struct swsusp_info *)buffer;
if (!enough_swap(header->pages)) {
printf("suspend: Not enough free swap\n");
return -ENOSPC;
}
swap_map = alloc_swap_map(header->pages);
if (!swap_map)
return -ENOMEM;
init_swap_map_handle(&handle, fd, swap_map);
error = swap_map_write_page(&handle, header);
if (!error)
error = save_image(&handle, header->pages - 1);
if (error)
goto Free_image_entries;
swap_map = reverse_swap_map(swap_map);
error = save_swap_map(fd, swap_map, &start);
if (error)
goto Free_map_entries;
printf( "S" );
error = mark_swapfiles(fd, start);
printf( "|\n" );
if (error)
goto Free_map_entries;
Free_swap_map:
free_swap_map(swap_map);
fsync(fd);
close(fd);
return error;
Free_map_entries:
free_swap_map_entries(swap_map);
Free_image_entries:
free_image_entries(swap_map);
goto Free_swap_map;
}
int main(int argc, char *argv[])
{
char *snapshot_device_name, *resume_device_name;
int image_size;
int in_suspend;
resume_device_name = argc <= 2 ? RESUME_DEVICE : argv[2];
snapshot_device_name = argc <= 1 ? SNAPSHOT_DEVICE : argv[1];
sync();
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
fprintf(stderr, "Could not lock myself\n");
return 1;
}
image_size = DEFAULT_IMAGE_SIZE;
while (image_size >= 0) {
dev = open(snapshot_device_name, O_RDONLY);
if (dev < 0)
return -ENOENT;
if (set_image_size(dev, image_size)) {
fprintf(stderr, "Could not set the image size\n");
return errno;
}
if (freeze(dev)) {
fprintf(stderr, "Could not freeze processes\n");
return errno;
}
if (!atomic_snapshot(dev, &in_suspend)) {
if (!in_suspend) {
unfreeze(dev);
close(dev);
return 0;
}
if (!write_image(resume_device_name))
power_off();
else
image_size -= DEFAULT_IMAGE_SIZE;
}
unfreeze(dev);
close(dev);
}
return 0;
}
[-- Attachment #4: resume.c --]
[-- Type: text/x-csrc, Size: 5190 bytes --]
/*
* resume.c
*
* A simple user space resume handler for swsusp.
*
* Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
*
* This file is released under the GPLv2.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <syscall.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "swsusp.h"
static char buffer[PAGE_SIZE];
/**
* read_page - Read one page from a swap location.
* @fd: File handle of the resume partition
* @buf: Pointer to the area we're reading into.
* @off: Swap offset of the place to read from.
*/
static int read_page(int fd, void *buf, unsigned long off)
{
off_t offset;
int res = 0;
ssize_t cnt = 0;
if (off) {
offset = off * PAGE_SIZE;
if (lseek(fd, offset, SEEK_SET) == offset)
cnt = read(fd, buf, PAGE_SIZE);
if (cnt < PAGE_SIZE) {
if (cnt < 0)
res = cnt;
else
res = -EIO;
}
}
return res;
}
/**
* The following functions allow us to read data using a swap map
* in a file-alike way
*/
static inline void release_swap_map_reader(struct swap_map_handle *handle)
{
if (handle->cur)
free(handle->cur);
handle->cur = NULL;
}
static inline int get_swap_map_reader(struct swap_map_handle *handle,
int fd, unsigned long start)
{
int error;
if (!start)
return -EINVAL;
handle->cur = (struct swap_map_page *)malloc(PAGE_SIZE);
if (!handle->cur)
return -ENOMEM;
memset(handle->cur, 0, PAGE_SIZE);
error = read_page(fd, handle->cur, start);
if (error) {
release_swap_map_reader(handle);
return error;
}
handle->fd = fd;
handle->k = 0;
return 0;
}
static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf)
{
unsigned long offset;
int error;
if (!handle->cur)
return -EINVAL;
offset = handle->cur->entries[handle->k];
if (!offset)
return -EFAULT;
error = read_page(handle->fd, buf, offset);
if (error)
return error;
if (++handle->k >= MAP_PAGE_SIZE) {
handle->k = 0;
offset = handle->cur->next_swap;
if (!offset)
release_swap_map_reader(handle);
else
error = read_page(handle->fd, handle->cur, offset);
}
return error;
}
/**
* load_image - load the image using the swap map handle
* @handle and the snapshot handle @snapshot
* (assume there are @nr_pages pages to load)
*/
static inline int load_image(struct swap_map_handle *handle, int dev,
unsigned int nr_pages)
{
unsigned int m, n;
int ret;
int error = 0;
printf("Loading image data pages (%u pages) ... ", nr_pages);
m = nr_pages / 100;
if (!m)
m = 1;
n = 0;
do {
error = swap_map_read_page(handle, buffer);
if (error)
break;
if ((ret = write(dev, buffer, PAGE_SIZE)) < PAGE_SIZE) {
error = ret < 0 ? ret : -ENOSPC;
break;
}
if (!(n % m))
printf("\b\b\b\b%3d%%", n / m);
n++;
} while (n < nr_pages);
if (!error)
printf("\b\b\b\bdone\n");
return error;
}
static int read_image(int dev, char *resume_dev_name)
{
int fd, ret, error = 0;
struct swap_map_handle handle;
struct swsusp_info *header;
unsigned int nr_pages;
fd = open(resume_dev_name, O_RDWR | O_SYNC);
if (fd < 0) {
printf("resume: Could not open resume device\n");
return -ENOENT;
}
memset(&swsusp_header, 0, sizeof(swsusp_header));
ret = read(fd, &swsusp_header, PAGE_SIZE);
if (ret == PAGE_SIZE) {
if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
/* Reset swap signature now */
if (lseek(fd, 0, SEEK_SET) == 0) {
ret = write(fd, &swsusp_header, PAGE_SIZE);
if (ret < PAGE_SIZE)
error = ret < 0 ? ret : -EIO;
} else {
error = -EIO;
}
} else {
error = -EINVAL;
}
} else {
error = ret < 0 ? ret : -EIO;
}
if (!error) {
error = get_swap_map_reader(&handle, fd, swsusp_header.image);
if (!error) {
header = (struct swsusp_info *)buffer;
error = swap_map_read_page(&handle, header);
}
if (!error) {
nr_pages = header->pages - 1;
ret = write(dev, buffer, PAGE_SIZE);
if (ret < PAGE_SIZE)
error = ret < 0 ? ret : -EIO;
}
if (!error)
error = load_image(&handle, dev, nr_pages);
release_swap_map_reader(&handle);
}
fsync(fd);
close(fd);
if (!error)
printf("resume: Reading resume file was successful\n");
else
printf("resume: Error %d resuming\n", error);
return error;
}
int main(int argc, char *argv[])
{
char *snapshot_device_name, *resume_device_name;
int dev;
resume_device_name = argc <= 2 ? RESUME_DEVICE : argv[2];
snapshot_device_name = argc <= 1 ? SNAPSHOT_DEVICE : argv[1];
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
fprintf(stderr, "resume: Could not lock myself\n");
return 1;
}
dev = open(snapshot_device_name, O_WRONLY);
if (dev < 0)
return -ENOENT;
if (read_image(dev, resume_device_name)) {
fprintf(stderr, "resume: Could not read the image\n");
return errno;
}
if (freeze(dev)) {
fprintf(stderr, "resume: Could not freeze processes\n");
return errno;
}
atomic_restore(dev);
unfreeze(dev);
close(dev);
return 0;
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/RFT][PATCH -mm 1/4] swsusp: low level interface
2005-12-28 0:08 [RFC/RFT][PATCH -mm 0/4] swsusp: userland interface Rafael J. Wysocki
@ 2005-12-28 0:17 ` Rafael J. Wysocki
2005-12-28 0:25 ` [RFC/RFT][PATCH -mm 2/4] swsusp: userland interface Rafael J. Wysocki
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2005-12-28 0:17 UTC (permalink / raw)
To: Linux PM; +Cc: LKML, Pavel Machek
This patch introduces the low level interface that can be used for handling
the snapshot of the system memory by both the in-kernel swap-writing
and reading code of swsusp and the userland interface code introduced
by the next patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
kernel/power/disk.c | 12 -
kernel/power/power.h | 25 ++-
kernel/power/snapshot.c | 324 +++++++++++++++++++++++++++++++++++++++++
kernel/power/swsusp.c | 372 ++++++++++++------------------------------------
4 files changed, 439 insertions(+), 294 deletions(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/disk.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/disk.c 2005-12-27 21:49:58.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/disk.c 2005-12-27 21:53:03.000000000 +0100
@@ -26,9 +26,9 @@ extern suspend_disk_method_t pm_disk_mod
extern int swsusp_shrink_memory(void);
extern int swsusp_suspend(void);
-extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages);
+extern int swsusp_write(void);
extern int swsusp_check(void);
-extern int swsusp_read(struct pbe **pblist_ptr);
+extern int swsusp_read(void);
extern void swsusp_close(void);
extern int swsusp_resume(void);
@@ -70,10 +70,6 @@ static void power_down(suspend_disk_meth
while(1);
}
-
-static int in_suspend __nosavedata = 0;
-
-
static inline void platform_finish(void)
{
if (pm_disk_mode == PM_DISK_PLATFORM) {
@@ -152,7 +148,7 @@ int pm_suspend_disk(void)
if (in_suspend) {
device_resume();
pr_debug("PM: writing image.\n");
- error = swsusp_write(pagedir_nosave, nr_copy_pages);
+ error = swsusp_write();
if (!error)
power_down(pm_disk_mode);
else {
@@ -223,7 +219,7 @@ static int software_resume(void)
pr_debug("PM: Reading swsusp image.\n");
- if ((error = swsusp_read(&pagedir_nosave))) {
+ if ((error = swsusp_read())) {
swsusp_free();
goto Thaw;
}
Index: linux-2.6.15-rc5-mm3/kernel/power/power.h
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/power.h 2005-12-27 21:49:58.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/power.h 2005-12-28 01:17:06.000000000 +0100
@@ -54,15 +54,26 @@ extern struct pbe *pagedir_nosave;
/* Preferred image size in MB (default 500) */
extern unsigned int image_size;
+extern int in_suspend;
+
extern asmlinkage int swsusp_arch_suspend(void);
extern asmlinkage int swsusp_arch_resume(void);
extern unsigned int count_data_pages(void);
-extern void free_pagedir(struct pbe *pblist);
-extern void release_eaten_pages(void);
-extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed);
extern void swsusp_free(void);
-extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed);
-extern unsigned int snapshot_nr_pages(void);
-extern struct pbe *snapshot_pblist(void);
-extern void snapshot_pblist_set(struct pbe *pblist);
+
+struct snapshot_handle {
+ loff_t offset;
+ unsigned int page;
+ unsigned int page_offset;
+ unsigned int prev;
+ struct pbe *pbe;
+ void *buffer;
+ unsigned int buf_offset;
+};
+
+#define data_of(handle) ((handle).buffer + (handle).buf_offset)
+
+extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
+extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
+int snapshot_image_ready(struct snapshot_handle *handle);
Index: linux-2.6.15-rc5-mm3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/swsusp.c 2005-12-27 21:49:58.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2005-12-28 01:16:44.000000000 +0100
@@ -77,6 +77,8 @@
*/
unsigned int image_size = 500;
+int in_suspend __nosavedata = 0;
+
#ifdef CONFIG_HIGHMEM
unsigned int count_highmem_pages(void);
int save_highmem(void);
@@ -98,8 +100,6 @@ static struct swsusp_header {
char sig[10];
} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
-static struct swsusp_info swsusp_info;
-
/*
* Saving part...
*/
@@ -149,7 +149,7 @@ static inline int is_resume_device(const
swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
}
-static int swsusp_swap_check(void) /* This is called before saving image */
+int swsusp_get_swap_index(void)
{
int i;
@@ -161,36 +161,42 @@ static int swsusp_swap_check(void) /* Th
continue;
if (is_resume_device(swap_info + i)) {
spin_unlock(&swap_lock);
- root_swap = i;
- return 0;
+ return i;
}
}
spin_unlock(&swap_lock);
return -ENODEV;
}
+static int swsusp_swap_check(void) /* This is called before saving image */
+{
+ int res = swsusp_get_swap_index();
+
+ if (res >= 0) {
+ root_swap = res;
+ return 0;
+ }
+ return res;
+}
+
/**
* write_page - Write one page to a fresh swap location.
- * @addr: Address we're writing.
+ * @buf: Address we're writing.
* @loc: Place to store the entry we used.
- *
- * Allocate a new swap entry and 'sync' it. Note we discard -EIO
- * errors. That is an artifact left over from swsusp. It did not
- * check the return of rw_swap_page_sync() at all, since most pages
- * written back to swap would return -EIO.
- * This is a partial improvement, since we will at least return other
- * errors, though we need to eventually fix the damn code.
*/
-static int write_page(unsigned long addr, swp_entry_t *loc)
+
+static int write_page(void *buf, swp_entry_t *loc)
{
swp_entry_t entry;
int error = -ENOSPC;
entry = get_swap_page_of_type(root_swap);
if (swp_offset(entry)) {
- error = rw_swap_page_sync(WRITE, entry, virt_to_page(addr));
- if (!error || error == -EIO)
+ error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
+ if (!error)
*loc = entry;
+ else
+ swap_free(entry);
}
return error;
}
@@ -309,7 +315,7 @@ static int save_swap_map(struct swap_map
while (swap_map) {
swap_map->next_swap = entry;
- if ((error = write_page((unsigned long)swap_map, &entry)))
+ if ((error = write_page(swap_map, &entry)))
return error;
swap_map = swap_map->next;
}
@@ -351,12 +357,11 @@ static inline void init_swap_map_handle(
handle->k = 0;
}
-static inline int swap_map_write_page(struct swap_map_handle *handle,
- unsigned long addr)
+static inline int swap_map_write_page(struct swap_map_handle *handle, void *buf)
{
int error;
- error = write_page(addr, handle->cur->entries + handle->k);
+ error = write_page(buf, handle->cur->entries + handle->k);
if (error)
return error;
if (++handle->k >= MAP_PAGE_SIZE) {
@@ -367,17 +372,15 @@ static inline int swap_map_write_page(st
}
/**
- * save_image_data - save the data pages pointed to by the PBEs
- * from the list @pblist using the swap map handle @handle
- * (assume there are @nr_pages data pages to save)
+ * save_image - save the suspend image data
*/
-static int save_image_data(struct pbe *pblist,
- struct swap_map_handle *handle,
- unsigned int nr_pages)
+static int save_image(struct swap_map_handle *handle,
+ struct snapshot_handle *snapshot,
+ unsigned int nr_pages)
{
unsigned int m;
- struct pbe *p;
+ int ret;
int error = 0;
printk("Saving image data pages (%u pages) ... ", nr_pages);
@@ -385,96 +388,39 @@ static int save_image_data(struct pbe *p
if (!m)
m = 1;
nr_pages = 0;
- for_each_pbe (p, pblist) {
- error = swap_map_write_page(handle, p->address);
- if (error)
- break;
- if (!(nr_pages % m))
- printk("\b\b\b\b%3d%%", nr_pages / m);
- nr_pages++;
- }
+ do {
+ ret = snapshot_read_next(snapshot, PAGE_SIZE);
+ if (ret > 0) {
+ error = swap_map_write_page(handle, data_of(*snapshot));
+ if (error)
+ break;
+ if (!(nr_pages % m))
+ printk("\b\b\b\b%3d%%", nr_pages / m);
+ nr_pages++;
+ }
+ } while (ret > 0);
if (!error)
printk("\b\b\b\bdone\n");
return error;
}
-static void dump_info(void)
-{
- pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code);
- pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages);
- pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname);
- pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename);
- pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release);
- pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version);
- pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine);
- pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname);
- pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus);
- pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages);
- pr_debug(" swsusp: Total: %ld Pages\n", swsusp_info.pages);
-}
-
-static void init_header(unsigned int nr_pages)
-{
- memset(&swsusp_info, 0, sizeof(swsusp_info));
- swsusp_info.version_code = LINUX_VERSION_CODE;
- swsusp_info.num_physpages = num_physpages;
- memcpy(&swsusp_info.uts, &system_utsname, sizeof(system_utsname));
-
- swsusp_info.cpus = num_online_cpus();
- swsusp_info.image_pages = nr_pages;
- swsusp_info.pages = nr_pages +
- ((nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
-}
-
/**
- * pack_orig_addresses - the .orig_address fields of the PBEs from the
- * list starting at @pbe are stored in the array @buf[] (1 page)
+ * swsusp_available_swap - check the total amount of swap
+ * space avaiable from given swap partition/file
*/
-static inline struct pbe *pack_orig_addresses(unsigned long *buf,
- struct pbe *pbe)
+unsigned int swsusp_available_swap(unsigned int swap)
{
- int j;
-
- for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
- buf[j] = pbe->orig_address;
- pbe = pbe->next;
- }
- if (!pbe)
- for (; j < PAGE_SIZE / sizeof(long); j++)
- buf[j] = 0;
- return pbe;
-}
-
-/**
- * save_image_metadata - save the .orig_address fields of the PBEs
- * from the list @pblist using the swap map handle @handle
- */
-
-static int save_image_metadata(struct pbe *pblist,
- struct swap_map_handle *handle)
-{
- unsigned long *buf;
unsigned int n = 0;
- struct pbe *p;
- int error = 0;
- printk("Saving image metadata ... ");
- buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC);
- if (!buf)
- return -ENOMEM;
- p = pblist;
- while (p) {
- p = pack_orig_addresses(buf, p);
- error = swap_map_write_page(handle, (unsigned long)buf);
- if (error)
- break;
- n++;
+ if (swap < MAX_SWAPFILES) {
+ spin_lock(&swap_lock);
+ if (swap_info[swap].flags & SWP_WRITEOK)
+ n = swap_info[swap].pages -
+ swap_info[swap].inuse_pages;
+ spin_unlock(&swap_lock);
}
- free_page((unsigned long)buf);
- if (!error)
- printk("done (%u pages saved)\n", n);
- return error;
+ return n;
}
/**
@@ -486,8 +432,7 @@ static int save_image_metadata(struct pb
static int enough_swap(unsigned int nr_pages)
{
- unsigned int free_swap = swap_info[root_swap].pages -
- swap_info[root_swap].inuse_pages;
+ unsigned int free_swap = swsusp_available_swap(root_swap);
pr_debug("swsusp: free swap pages: %u\n", free_swap);
return free_swap > (nr_pages + PAGES_FOR_IO +
@@ -503,10 +448,13 @@ static int enough_swap(unsigned int nr_p
* correctly, we'll mark system clean, anyway.)
*/
-int swsusp_write(struct pbe *pblist, unsigned int nr_pages)
+int swsusp_write(void)
{
struct swap_map_page *swap_map;
struct swap_map_handle handle;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
+ unsigned int nr_pages;
swp_entry_t start;
int error;
@@ -514,22 +462,24 @@ int swsusp_write(struct pbe *pblist, uns
printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n");
return error;
}
+ memset(&snapshot, 0, sizeof(struct snapshot_handle));
+ error = snapshot_read_next(&snapshot, PAGE_SIZE);
+ if (error < PAGE_SIZE)
+ return error < 0 ? error : -EFAULT;
+ header = (struct swsusp_info *)data_of(snapshot);
+ nr_pages = header->image_pages;
if (!enough_swap(nr_pages)) {
printk(KERN_ERR "swsusp: Not enough free swap\n");
return -ENOSPC;
}
-
- init_header(nr_pages);
- swap_map = alloc_swap_map(swsusp_info.pages);
+ swap_map = alloc_swap_map(header->pages);
if (!swap_map)
return -ENOMEM;
init_swap_map_handle(&handle, swap_map);
- error = swap_map_write_page(&handle, (unsigned long)&swsusp_info);
+ error = swap_map_write_page(&handle, header);
if (!error)
- error = save_image_metadata(pblist, &handle);
- if (!error)
- error = save_image_data(pblist, &handle, nr_pages);
+ error = save_image(&handle, &snapshot, nr_pages);
if (error)
goto Free_image_entries;
@@ -538,7 +488,6 @@ int swsusp_write(struct pbe *pblist, uns
if (error)
goto Free_map_entries;
- dump_info();
printk( "S" );
error = mark_swapfiles(start);
printk( "|\n" );
@@ -663,45 +612,6 @@ int swsusp_resume(void)
return error;
}
-/**
- * mark_unsafe_pages - mark the pages that cannot be used for storing
- * the image during resume, because they conflict with the pages that
- * had been used before suspend
- */
-
-static void mark_unsafe_pages(struct pbe *pblist)
-{
- struct zone *zone;
- unsigned long zone_pfn;
- struct pbe *p;
-
- if (!pblist) /* a sanity check */
- return;
-
- /* Clear page flags */
- for_each_zone (zone) {
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
- if (pfn_valid(zone_pfn + zone->zone_start_pfn))
- ClearPageNosaveFree(pfn_to_page(zone_pfn +
- zone->zone_start_pfn));
- }
-
- /* Mark orig addresses */
- for_each_pbe (p, pblist)
- SetPageNosaveFree(virt_to_page(p->orig_address));
-
-}
-
-static void copy_page_backup_list(struct pbe *dst, struct pbe *src)
-{
- /* We assume both lists contain the same number of elements */
- while (src) {
- dst->orig_address = src->orig_address;
- dst = dst->next;
- src = src->next;
- }
-}
-
/*
* Using bio to read from swap.
* This code requires a bit more work than just using buffer heads
@@ -816,7 +726,7 @@ static inline int swap_map_read_page(str
return -EINVAL;
offset = swp_offset(handle->cur->entries[handle->k]);
if (!offset)
- return -EINVAL;
+ return -EFAULT;
error = bio_read_page(offset, buf);
if (error)
return error;
@@ -831,118 +741,49 @@ static inline int swap_map_read_page(str
return error;
}
-static int check_header(void)
-{
- char *reason = NULL;
-
- dump_info();
- if (swsusp_info.version_code != LINUX_VERSION_CODE)
- reason = "kernel version";
- if (swsusp_info.num_physpages != num_physpages)
- reason = "memory size";
- if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname))
- reason = "system type";
- if (strcmp(swsusp_info.uts.release,system_utsname.release))
- reason = "kernel release";
- if (strcmp(swsusp_info.uts.version,system_utsname.version))
- reason = "version";
- if (strcmp(swsusp_info.uts.machine,system_utsname.machine))
- reason = "machine";
- if (reason) {
- printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason);
- return -EPERM;
- }
- return 0;
-}
-
/**
- * load_image_data - load the image data using the swap map handle
- * @handle and store them using the page backup list @pblist
+ * load_image - load the image using the swap map handle
+ * @handle and the snapshot handle @snapshot
* (assume there are @nr_pages pages to load)
*/
-static int load_image_data(struct pbe *pblist,
- struct swap_map_handle *handle,
- unsigned int nr_pages)
+static int load_image(struct swap_map_handle *handle,
+ struct snapshot_handle *snapshot,
+ unsigned int nr_pages)
{
- int error;
unsigned int m;
- struct pbe *p;
+ int ret;
+ int error = 0;
- if (!pblist)
- return -EINVAL;
printk("Loading image data pages (%u pages) ... ", nr_pages);
m = nr_pages / 100;
if (!m)
m = 1;
nr_pages = 0;
- p = pblist;
- while (p) {
- error = swap_map_read_page(handle, (void *)p->address);
- if (error)
- break;
- p = p->next;
- if (!(nr_pages % m))
- printk("\b\b\b\b%3d%%", nr_pages / m);
- nr_pages++;
- }
+ do {
+ ret = snapshot_write_next(snapshot, PAGE_SIZE);
+ if (ret > 0) {
+ error = swap_map_read_page(handle, data_of(*snapshot));
+ if (error)
+ break;
+ if (!(nr_pages % m))
+ printk("\b\b\b\b%3d%%", nr_pages / m);
+ nr_pages++;
+ }
+ } while (ret > 0);
if (!error)
printk("\b\b\b\bdone\n");
+ if (!snapshot_image_ready(snapshot))
+ error = -ENODATA;
return error;
}
-/**
- * unpack_orig_addresses - copy the elements of @buf[] (1 page) to
- * the PBEs in the list starting at @pbe
- */
-
-static inline struct pbe *unpack_orig_addresses(unsigned long *buf,
- struct pbe *pbe)
-{
- int j;
-
- for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
- pbe->orig_address = buf[j];
- pbe = pbe->next;
- }
- return pbe;
-}
-
-/**
- * load_image_metadata - load the image metadata using the swap map
- * handle @handle and put them into the PBEs in the list @pblist
- */
-
-static int load_image_metadata(struct pbe *pblist, struct swap_map_handle *handle)
-{
- struct pbe *p;
- unsigned long *buf;
- unsigned int n = 0;
- int error = 0;
-
- printk("Loading image metadata ... ");
- buf = (unsigned long *)get_zeroed_page(GFP_ATOMIC);
- if (!buf)
- return -ENOMEM;
- p = pblist;
- while (p) {
- error = swap_map_read_page(handle, buf);
- if (error)
- break;
- p = unpack_orig_addresses(buf, p);
- n++;
- }
- free_page((unsigned long)buf);
- if (!error)
- printk("done (%u pages loaded)\n", n);
- return error;
-}
-
-int swsusp_read(struct pbe **pblist_ptr)
+int swsusp_read(void)
{
int error;
- struct pbe *p, *pblist;
struct swap_map_handle handle;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
unsigned int nr_pages;
if (IS_ERR(resume_bdev)) {
@@ -950,36 +791,17 @@ int swsusp_read(struct pbe **pblist_ptr)
return PTR_ERR(resume_bdev);
}
+ memset(&snapshot, 0, sizeof(struct snapshot_handle));
+ error = snapshot_write_next(&snapshot, PAGE_SIZE);
+ if (error < PAGE_SIZE)
+ return error < 0 ? error : -EFAULT;
+ header = (struct swsusp_info *)data_of(snapshot);
error = get_swap_map_reader(&handle, swsusp_header.image);
if (!error)
- error = swap_map_read_page(&handle, &swsusp_info);
- if (!error)
- error = check_header();
- if (error)
- return error;
- nr_pages = swsusp_info.image_pages;
- p = alloc_pagedir(nr_pages, GFP_ATOMIC, 0);
- if (!p)
- return -ENOMEM;
- error = load_image_metadata(p, &handle);
+ error = swap_map_read_page(&handle, header);
if (!error) {
- mark_unsafe_pages(p);
- pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1);
- if (pblist)
- copy_page_backup_list(pblist, p);
- free_pagedir(p);
- if (!pblist)
- error = -ENOMEM;
-
- /* Allocate memory for the image and read the data from swap */
- if (!error)
- error = alloc_data_pages(pblist, GFP_ATOMIC, 1);
- if (!error) {
- release_eaten_pages();
- error = load_image_data(pblist, &handle, nr_pages);
- }
- if (!error)
- *pblist_ptr = pblist;
+ nr_pages = header->image_pages;
+ error = load_image(&handle, &snapshot, nr_pages);
}
release_swap_map_reader(&handle);
Index: linux-2.6.15-rc5-mm3/kernel/power/snapshot.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/snapshot.c 2005-12-27 21:49:58.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/snapshot.c 2005-12-28 01:16:43.000000000 +0100
@@ -10,6 +10,7 @@
*/
+#include <linux/version.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
@@ -35,6 +36,7 @@
struct pbe *pagedir_nosave;
unsigned int nr_copy_pages;
+unsigned int nr_meta_pages;
#ifdef CONFIG_HIGHMEM
unsigned int count_highmem_pages(void)
@@ -237,7 +239,7 @@ static void copy_data_pages(struct pbe *
* free_pagedir - free pages allocated with alloc_pagedir()
*/
-void free_pagedir(struct pbe *pblist)
+static void free_pagedir(struct pbe *pblist)
{
struct pbe *pbe;
@@ -303,7 +305,7 @@ struct eaten_page {
static struct eaten_page *eaten_pages = NULL;
-void release_eaten_pages(void)
+static inline void release_eaten_pages(void)
{
struct eaten_page *p, *q;
@@ -378,7 +380,6 @@ struct pbe *alloc_pagedir(unsigned int n
if (!nr_pages)
return NULL;
- pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages);
pblist = alloc_image_page(gfp_mask, safe_needed);
/* FIXME: rewrite this ugly loop */
for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages;
@@ -416,6 +417,9 @@ void swsusp_free(void)
}
}
}
+ nr_copy_pages = 0;
+ nr_meta_pages = 0;
+ pagedir_nosave = NULL;
}
@@ -439,7 +443,7 @@ static int enough_free_mem(unsigned int
(nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
}
-int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed)
+static int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed)
{
struct pbe *p;
@@ -506,7 +510,319 @@ asmlinkage int swsusp_save(void)
*/
nr_copy_pages = nr_pages;
+ nr_meta_pages = (nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT;
printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages);
return 0;
}
+
+static inline void init_header(struct swsusp_info *info)
+{
+ memset(info, 0, sizeof(struct swsusp_info));
+ info->version_code = LINUX_VERSION_CODE;
+ info->num_physpages = num_physpages;
+ memcpy(&info->uts, &system_utsname, sizeof(system_utsname));
+ info->cpus = num_online_cpus();
+ info->image_pages = nr_copy_pages;
+ info->pages = nr_copy_pages + nr_meta_pages + 1;
+}
+
+/**
+ * pack_orig_addresses - the .orig_address fields of the PBEs from the
+ * list starting at @pbe are stored in the array @buf[] (1 page)
+ */
+
+static inline struct pbe *pack_orig_addresses(unsigned long *buf, struct pbe *pbe)
+{
+ int j;
+
+ for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
+ buf[j] = pbe->orig_address;
+ pbe = pbe->next;
+ }
+ if (!pbe)
+ for (; j < PAGE_SIZE / sizeof(long); j++)
+ buf[j] = 0;
+ return pbe;
+}
+
+/**
+ * snapshot_read_next - used for reading the system memory snapshot.
+ *
+ * On the first call to it @handle should point to a zeroed
+ * snapshot_handle structure. The structure gets updated and a pointer
+ * to it should be passed to this function every next time.
+ *
+ * The @count parameter should contain the number of bytes the caller
+ * wants to read from the snapshot. It must not be zero.
+ *
+ * On success the function returns a positive number. Then, the caller
+ * is allowed to read up to the returned number of bytes from the memory
+ * location computed by the data_of() macro. The number returned
+ * may be smaller than @count, but this only happens if the read would
+ * cross a page boundary otherwise.
+ *
+ * The function returns 0 to indicate the end of data stream condition,
+ * and a negative number is returned on error. In such cases the
+ * structure pointed to by @handle is not updated and should not be used
+ * any more.
+ */
+
+int snapshot_read_next(struct snapshot_handle *handle, size_t count)
+{
+ static unsigned long *buffer;
+
+ if (handle->page > nr_meta_pages + nr_copy_pages)
+ return 0;
+ if (!buffer) {
+ /* This makes the buffer be freed by swsusp_free() */
+ buffer = alloc_image_page(GFP_ATOMIC, 0);
+ if (!buffer)
+ return -ENOMEM;
+ }
+ if (!handle->offset) {
+ init_header((struct swsusp_info *)buffer);
+ handle->buffer = buffer;
+ handle->pbe = pagedir_nosave;
+ }
+ if (handle->prev < handle->page) {
+ if (handle->page <= nr_meta_pages) {
+ handle->pbe = pack_orig_addresses(buffer, handle->pbe);
+ if (!handle->pbe)
+ handle->pbe = pagedir_nosave;
+ } else {
+ handle->buffer = (void *)handle->pbe->address;
+ handle->pbe = handle->pbe->next;
+ }
+ handle->prev = handle->page;
+ }
+ handle->buf_offset = handle->page_offset;
+ if (handle->page_offset + count >= PAGE_SIZE) {
+ count = PAGE_SIZE - handle->page_offset;
+ handle->page_offset = 0;
+ handle->page++;
+ } else {
+ handle->page_offset += count;
+ }
+ handle->offset += count;
+ return count;
+}
+
+/**
+ * mark_unsafe_pages - mark the pages that cannot be used for storing
+ * the image during resume, because they conflict with the pages that
+ * had been used before suspend
+ */
+
+static inline int mark_unsafe_pages(struct pbe *pblist)
+{
+ struct zone *zone;
+ unsigned long zone_pfn;
+ struct pbe *p;
+
+ if (!pblist) /* a sanity check */
+ return -EINVAL;
+
+ /* Clear page flags */
+ for_each_zone (zone) {
+ for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
+ if (pfn_valid(zone_pfn + zone->zone_start_pfn))
+ ClearPageNosaveFree(pfn_to_page(zone_pfn +
+ zone->zone_start_pfn));
+ }
+
+ /* Mark orig addresses */
+ for_each_pbe (p, pblist) {
+ if (virt_addr_valid(p->orig_address))
+ SetPageNosaveFree(virt_to_page(p->orig_address));
+ else
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static inline void copy_page_backup_list(struct pbe *dst, struct pbe *src)
+{
+ /* We assume both lists contain the same number of elements */
+ while (src) {
+ dst->orig_address = src->orig_address;
+ dst = dst->next;
+ src = src->next;
+ }
+}
+
+static inline int check_header(struct swsusp_info *info)
+{
+ char *reason = NULL;
+
+ if (info->version_code != LINUX_VERSION_CODE)
+ reason = "kernel version";
+ if (info->num_physpages != num_physpages)
+ reason = "memory size";
+ if (strcmp(info->uts.sysname,system_utsname.sysname))
+ reason = "system type";
+ if (strcmp(info->uts.release,system_utsname.release))
+ reason = "kernel release";
+ if (strcmp(info->uts.version,system_utsname.version))
+ reason = "version";
+ if (strcmp(info->uts.machine,system_utsname.machine))
+ reason = "machine";
+ if (reason) {
+ printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason);
+ return -EPERM;
+ }
+ return 0;
+}
+
+/**
+ * load header - check the image header and copy data from it
+ */
+
+static inline int load_header(struct snapshot_handle *handle,
+ struct swsusp_info *info)
+{
+ int error;
+ struct pbe *pblist;
+
+ error = check_header(info);
+ if (!error) {
+ pblist = alloc_pagedir(info->image_pages, GFP_ATOMIC, 0);
+ if (!pblist)
+ return -ENOMEM;
+ pagedir_nosave = pblist;
+ handle->pbe = pblist;
+ nr_copy_pages = info->image_pages;
+ nr_meta_pages = info->pages - info->image_pages - 1;
+ }
+ return error;
+}
+
+/**
+ * unpack_orig_addresses - copy the elements of @buf[] (1 page) to
+ * the PBEs in the list starting at @pbe
+ */
+
+static inline struct pbe *unpack_orig_addresses(unsigned long *buf,
+ struct pbe *pbe)
+{
+ int j;
+
+ for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) {
+ pbe->orig_address = buf[j];
+ pbe = pbe->next;
+ }
+ return pbe;
+}
+
+/**
+ * create_image - use metadata contained in the PBE list
+ * pointed to by pagedir_nosave to mark the pages that will
+ * be overwritten in the process of restoring the system
+ * memory state from the image and allocate memory for
+ * the image avoiding these pages
+ */
+
+static inline int create_image(struct snapshot_handle *handle)
+{
+ int error = 0;
+ struct pbe *p, *pblist;
+
+ p = pagedir_nosave;
+ error = mark_unsafe_pages(p);
+ if (!error) {
+ pblist = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1);
+ if (pblist)
+ copy_page_backup_list(pblist, p);
+ free_pagedir(p);
+ if (!pblist)
+ error = -ENOMEM;
+ }
+ if (!error)
+ error = alloc_data_pages(pblist, GFP_ATOMIC, 1);
+ if (!error) {
+ release_eaten_pages();
+ pagedir_nosave = pblist;
+ } else {
+ pagedir_nosave = NULL;
+ handle->pbe = NULL;
+ nr_copy_pages = 0;
+ nr_meta_pages = 0;
+ }
+ return error;
+}
+
+/**
+ * snapshot_write_next - used for writing the system memory snapshot.
+ *
+ * On the first call to it @handle should point to a zeroed
+ * snapshot_handle structure. The structure gets updated and a pointer
+ * to it should be passed to this function every next time.
+ *
+ * The @count parameter should contain the number of bytes the caller
+ * wants to write to the image. It must not be zero.
+ *
+ * On success the function returns a positive number. Then, the caller
+ * is allowed to write up to the returned number of bytes to the memory
+ * location computed by the data_of() macro. The number returned
+ * may be smaller than @count, but this only happens if the write would
+ * cross a page boundary otherwise.
+ *
+ * The function returns 0 to indicate the "end of file" condition,
+ * and a negative number is returned on error. In such cases the
+ * structure pointed to by @handle is not updated and should not be used
+ * any more.
+ */
+
+int snapshot_write_next(struct snapshot_handle *handle, size_t count)
+{
+ static unsigned long *buffer;
+ int error = 0;
+
+ if (handle->prev && handle->page > nr_meta_pages + nr_copy_pages)
+ return 0;
+ if (!buffer) {
+ /* This makes the buffer be freed by swsusp_free() */
+ buffer = alloc_image_page(GFP_ATOMIC, 0);
+ if (!buffer)
+ return -ENOMEM;
+ }
+ if (!handle->offset)
+ handle->buffer = buffer;
+ if (handle->prev < handle->page) {
+ if (!handle->prev) {
+ error = load_header(handle, (struct swsusp_info *)buffer);
+ if (error)
+ return error;
+ } else if (handle->prev <= nr_meta_pages) {
+ handle->pbe = unpack_orig_addresses(buffer, handle->pbe);
+ if (!handle->pbe) {
+ error = create_image(handle);
+ if (error)
+ return error;
+ handle->pbe = pagedir_nosave;
+ handle->buffer = (void *)handle->pbe->address;
+ }
+ } else {
+ handle->pbe = handle->pbe->next;
+ handle->buffer = (void *)handle->pbe->address;
+ }
+ handle->prev = handle->page;
+ }
+ handle->buf_offset = handle->page_offset;
+ if (handle->page_offset + count >= PAGE_SIZE) {
+ count = PAGE_SIZE - handle->page_offset;
+ handle->page_offset = 0;
+ handle->page++;
+ } else {
+ handle->page_offset += count;
+ }
+ handle->offset += count;
+ return count;
+}
+
+int snapshot_image_ready(struct snapshot_handle *handle)
+{
+ return !(!handle->pbe || handle->pbe->next ||
+ handle->page <= nr_meta_pages + nr_copy_pages);
+}
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/RFT][PATCH -mm 2/4] swsusp: userland interface
2005-12-28 0:08 [RFC/RFT][PATCH -mm 0/4] swsusp: userland interface Rafael J. Wysocki
2005-12-28 0:17 ` [RFC/RFT][PATCH -mm 1/4] swsusp: low level interface Rafael J. Wysocki
@ 2005-12-28 0:25 ` Rafael J. Wysocki
2005-12-28 0:28 ` [RFC/RFT][PATCH -mm 3/4] swsusp: separate swap-writing and reading code Rafael J. Wysocki
2005-12-28 0:36 ` [RFC/RFT][PATCH -mm 4/4] swsusp: move highmem-handling code to swsusp.c Rafael J. Wysocki
3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2005-12-28 0:25 UTC (permalink / raw)
To: Linux PM; +Cc: LKML
[-- Attachment #1: Type: text/plain, Size: 9789 bytes --]
This patch adds a user space interface for swsusp. The interface is based
on the special character device allowing user space processes to perform
suspend and resume-related operations with the help of some ioctls and
the read()/write() functions. Additionally it allows these processes to
allocate and/or free swap pages so that they know which sectors of
the resume partition are available to them.
Currently the major number of the device is allocated dynamically,
so it is exported via sysfs for convenience, but I'd like the device to
have a well-defined major number, if possible/acceptable.
The interface uses the same low-level snapshot-handling functions that
are used by the in-kernel swap-writing and reading code of swsusp.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
init/do_mounts_initrd.c | 1
kernel/power/Makefile | 2
kernel/power/power.h | 11 +
kernel/power/user.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 289 insertions(+), 1 deletion(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/user.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15-rc5-mm3/kernel/power/user.c 2005-12-28 01:17:31.000000000 +0100
@@ -0,0 +1,276 @@
+/*
+ * linux/kernel/power/user.c
+ *
+ * This file provides the user space interface for software suspend/resume.
+ *
+ * Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#include <linux/suspend.h>
+#include <linux/syscalls.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/swapops.h>
+#include <linux/pm.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+
+#include "power.h"
+
+extern int swsusp_get_swap_index(void);
+extern unsigned int swsusp_available_swap(unsigned int swap);
+extern int swsusp_shrink_memory(void);
+extern int swsusp_suspend(void);
+extern int swsusp_resume(void);
+
+struct snapshot_dev {
+ char *name;
+ dev_t devno;
+ struct cdev cdev;
+ struct snapshot_handle handle;
+ int swap;
+ int mode;
+ int frozen;
+};
+
+static atomic_t device_available = ATOMIC_INIT(1);
+
+int snapshot_open(struct inode *inode, struct file *filp)
+{
+ struct snapshot_dev *data;
+
+ if (!atomic_dec_and_test(&device_available)) {
+ atomic_inc(&device_available);
+ return -EBUSY;
+ }
+
+ if ((filp->f_flags & O_ACCMODE) == O_RDWR)
+ return -ENOSYS;
+
+ nonseekable_open(inode, filp);
+ data = container_of(inode->i_cdev, struct snapshot_dev, cdev);
+ filp->private_data = data;
+ memset(&data->handle, 0, sizeof(struct snapshot_handle));
+ if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
+ data->swap = swsusp_get_swap_index();
+ data->mode = O_RDONLY;
+ } else {
+ data->swap = -1;
+ data->mode = O_WRONLY;
+ }
+ data->frozen = 0;
+
+ return 0;
+}
+
+int snapshot_release(struct inode *inode, struct file *filp)
+{
+ swsusp_free();
+ atomic_inc(&device_available);
+ return 0;
+}
+
+static ssize_t snapshot_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *offp)
+{
+ struct snapshot_dev *data;
+ ssize_t res;
+
+ data = filp->private_data;
+ res = snapshot_read_next(&data->handle, count);
+ if (res > 0) {
+ if (copy_to_user(buf, data_of(data->handle), res))
+ res = -EFAULT;
+ else
+ *offp = data->handle.offset;
+ }
+ return res;
+}
+
+static ssize_t snapshot_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offp)
+{
+ struct snapshot_dev *data;
+ ssize_t res;
+
+ data = filp->private_data;
+ res = snapshot_write_next(&data->handle, count);
+ if (res > 0) {
+ if (copy_from_user(data_of(data->handle), buf, res))
+ res = -EFAULT;
+ else
+ *offp = data->handle.offset;
+ }
+ return res;
+}
+
+static int snapshot_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int error = 0;
+ struct snapshot_dev *data;
+ unsigned long offset;
+ unsigned int n;
+
+ if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
+ return -ENOTTY;
+ if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
+ return -ENOTTY;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ data = filp->private_data;
+
+ switch (cmd) {
+
+ case SNAPSHOT_IOCFREEZE:
+ sys_sync();
+ down(&pm_sem);
+ disable_nonboot_cpus();
+ if (freeze_processes())
+ error = -EBUSY;
+ up(&pm_sem);
+ if (!error)
+ data->frozen = 1;
+ break;
+
+ case SNAPSHOT_IOCUNFREEZE:
+ down(&pm_sem);
+ thaw_processes();
+ enable_nonboot_cpus();
+ up(&pm_sem);
+ data->frozen = 0;
+ break;
+
+ case SNAPSHOT_IOCATOMIC_SNAPSHOT:
+ if (data->mode != O_RDONLY || !data->frozen) {
+ error = -EPERM;
+ break;
+ }
+ down(&pm_sem);
+ pm_prepare_console();
+ /* Free memory before shutting down devices. */
+ error = swsusp_shrink_memory();
+ if (!error) {
+ error = device_suspend(PMSG_FREEZE);
+ if (!error) {
+ in_suspend = 1;
+ error = swsusp_suspend();
+ device_resume();
+ }
+ }
+ pm_restore_console();
+ up(&pm_sem);
+ if (!error)
+ error = put_user(in_suspend, (unsigned int __user *)arg);
+ break;
+
+ case SNAPSHOT_IOCATOMIC_RESTORE:
+ if (data->mode != O_WRONLY || !data->frozen ||
+ !snapshot_image_ready(&data->handle)) {
+ error = -EPERM;
+ break;
+ }
+ down(&pm_sem);
+ pm_prepare_console();
+ error = device_suspend(PMSG_FREEZE);
+ if (!error) {
+ mb();
+ error = swsusp_resume();
+ device_resume();
+ }
+ pm_restore_console();
+ up(&pm_sem);
+ break;
+
+ case SNAPSHOT_IOCSET_IMAGE_SIZE:
+ image_size = arg;
+ break;
+
+ case SNAPSHOT_IOCAVAIL_SWAP:
+ n = swsusp_available_swap(data->swap);
+ error = put_user(n, (unsigned int __user *)arg);
+ break;
+
+ case SNAPSHOT_IOCGET_SWAP_PAGE:
+ if (data->swap >= 0 && data->swap < MAX_SWAPFILES) {
+ offset = swp_offset(get_swap_page_of_type(data->swap));
+ if (offset)
+ error = put_user(offset, (unsigned long __user *)arg);
+ else
+ error = -ENOSPC;
+ } else {
+ error = -ENODEV;
+ }
+ break;
+
+ case SNAPSHOT_IOCFREE_SWAP_PAGE:
+ if (data->swap >= 0 && data->swap < MAX_SWAPFILES)
+ swap_free(swp_entry(data->swap, arg));
+ else
+ error = -ENODEV;
+ break;
+
+ default:
+ error = -ENOTTY;
+
+ }
+
+ return error;
+}
+
+static struct file_operations snapshot_fops = {
+ .open = snapshot_open,
+ .release = snapshot_release,
+ .read = snapshot_read,
+ .write = snapshot_write,
+ .llseek = no_llseek,
+ .ioctl = snapshot_ioctl,
+};
+
+static struct snapshot_dev interface = {
+ .name = "snapshot",
+};
+
+static ssize_t snapshot_show(struct subsystem * subsys, char *buf)
+{
+ return sprintf(buf, "%d:%d\n", MAJOR(interface.devno),
+ MINOR(interface.devno));
+}
+
+static struct subsys_attribute snapshot_attr = {
+ .attr = {
+ .name = __stringify(snapshot),
+ .mode = S_IRUGO,
+ },
+ .show = snapshot_show,
+};
+
+static int __init snapshot_dev_init(void)
+{
+ int error;
+
+ error = alloc_chrdev_region(&interface.devno, 0, 1, interface.name);
+ if (error)
+ return error;
+ cdev_init(&interface.cdev, &snapshot_fops);
+ interface.cdev.ops = &snapshot_fops;
+ error = cdev_add(&interface.cdev, interface.devno, 1);
+ if (error)
+ goto Unregister;
+ error = sysfs_create_file(&power_subsys.kset.kobj, &snapshot_attr.attr);
+ if (!error)
+ return 0;
+ cdev_del(&interface.cdev);
+Unregister:
+ unregister_chrdev_region(interface.devno, 1);
+ return error;
+};
+
+late_initcall(snapshot_dev_init);
Index: linux-2.6.15-rc5-mm3/kernel/power/Makefile
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/Makefile 2005-12-28 01:16:46.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/Makefile 2005-12-28 01:17:31.000000000 +0100
@@ -5,7 +5,7 @@ endif
obj-y := main.o process.o console.o
obj-$(CONFIG_PM_LEGACY) += pm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o user.o
obj-$(CONFIG_SUSPEND_SMP) += smp.o
Index: linux-2.6.15-rc5-mm3/init/do_mounts_initrd.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/init/do_mounts_initrd.c 2005-12-28 01:16:46.000000000 +0100
+++ linux-2.6.15-rc5-mm3/init/do_mounts_initrd.c 2005-12-28 01:17:31.000000000 +0100
@@ -56,6 +56,7 @@ static void __init handle_initrd(void)
sys_chroot(".");
mount_devfs_fs ();
+ current->flags |= PF_NOFREEZE;
pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
if (pid > 0) {
while (pid != sys_wait4(-1, NULL, 0, NULL))
Index: linux-2.6.15-rc5-mm3/kernel/power/power.h
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/power.h 2005-12-28 01:17:06.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/power.h 2005-12-28 01:17:53.000000000 +0100
@@ -77,3 +77,14 @@ struct snapshot_handle {
extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
int snapshot_image_ready(struct snapshot_handle *handle);
+
+#define SNAPSHOT_IOC_MAGIC '3'
+#define SNAPSHOT_IOCFREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
+#define SNAPSHOT_IOCUNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
+#define SNAPSHOT_IOCATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
+#define SNAPSHOT_IOCATOMIC_RESTORE _IO(SNAPSHOT_IOC_MAGIC, 4)
+#define SNAPSHOT_IOCSET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 5, unsigned long)
+#define SNAPSHOT_IOCAVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 6, void *)
+#define SNAPSHOT_IOCGET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
+#define SNAPSHOT_IOCFREE_SWAP_PAGE _IOW(SNAPSHOT_IOC_MAGIC, 8, unsigned long)
+#define SNAPSHOT_IOC_MAXNR 8
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/RFT][PATCH -mm 3/4] swsusp: separate swap-writing and reading code
2005-12-28 0:08 [RFC/RFT][PATCH -mm 0/4] swsusp: userland interface Rafael J. Wysocki
2005-12-28 0:17 ` [RFC/RFT][PATCH -mm 1/4] swsusp: low level interface Rafael J. Wysocki
2005-12-28 0:25 ` [RFC/RFT][PATCH -mm 2/4] swsusp: userland interface Rafael J. Wysocki
@ 2005-12-28 0:28 ` Rafael J. Wysocki
2005-12-28 0:36 ` [RFC/RFT][PATCH -mm 4/4] swsusp: move highmem-handling code to swsusp.c Rafael J. Wysocki
3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2005-12-28 0:28 UTC (permalink / raw)
To: Linux PM; +Cc: LKML
[-- Attachment #1: Type: text/plain, Size: 34791 bytes --]
This patch moves the in-kernel swap-swap-writing and reading code to
a separate file.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
kernel/power/Makefile | 2
kernel/power/swap.c | 641 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/power/swsusp.c | 629 -------------------------------------------------
3 files changed, 644 insertions(+), 628 deletions(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/Makefile
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/Makefile 2005-12-27 22:33:08.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/Makefile 2005-12-27 23:39:20.000000000 +0100
@@ -5,7 +5,7 @@ endif
obj-y := main.o process.o console.o
obj-$(CONFIG_PM_LEGACY) += pm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o user.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o user.o swap.o
obj-$(CONFIG_SUSPEND_SMP) += smp.o
Index: linux-2.6.15-rc5-mm3/kernel/power/swap.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15-rc5-mm3/kernel/power/swap.c 2005-12-27 23:44:45.000000000 +0100
@@ -0,0 +1,641 @@
+/*
+ * linux/kernel/power/swap.c
+ *
+ * This file provides functions for reading the suspend image from
+ * and writing it to a swap partition.
+ *
+ * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz>
+ * Copyright (C) 2005 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/genhd.h>
+#include <linux/device.h>
+#include <linux/buffer_head.h>
+#include <linux/bio.h>
+#include <linux/swap.h>
+#include <linux/swapops.h>
+#include <linux/pm.h>
+
+#include "power.h"
+
+extern int swsusp_get_swap_index(void);
+extern unsigned int swsusp_available_swap(unsigned int swap);
+
+extern char resume_file[];
+
+#define SWSUSP_SIG "S1SUSPEND"
+
+static struct swsusp_header {
+ char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)];
+ swp_entry_t image;
+ char orig_sig[10];
+ char sig[10];
+} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
+
+/*
+ * Saving part...
+ */
+
+static unsigned short root_swap = 0xffff;
+
+static int mark_swapfiles(swp_entry_t start)
+{
+ int error;
+
+ rw_swap_page_sync(READ,
+ swp_entry(root_swap, 0),
+ virt_to_page((unsigned long)&swsusp_header));
+ if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) ||
+ !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
+ memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
+ memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
+ swsusp_header.image = start;
+ error = rw_swap_page_sync(WRITE,
+ swp_entry(root_swap, 0),
+ virt_to_page((unsigned long)
+ &swsusp_header));
+ } else {
+ pr_debug("swsusp: Partition is not swap space.\n");
+ error = -ENODEV;
+ }
+ return error;
+}
+
+static int swsusp_swap_check(void) /* This is called before saving image */
+{
+ int res = swsusp_get_swap_index();
+
+ if (res >= 0) {
+ root_swap = res;
+ return 0;
+ }
+ return res;
+}
+
+/**
+ * write_page - Write one page to a fresh swap location.
+ * @buf: Address we're writing.
+ * @loc: Place to store the entry we used.
+ */
+
+static int write_page(void *buf, swp_entry_t *loc)
+{
+ swp_entry_t entry;
+ int error = -ENOSPC;
+
+ entry = get_swap_page_of_type(root_swap);
+ if (swp_offset(entry)) {
+ error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
+ if (!error)
+ *loc = entry;
+ else
+ swap_free(entry);
+ }
+ return error;
+}
+
+/**
+ * Swap map-handling functions
+ *
+ * The swap map is a data structure used for keeping track of each page
+ * written to the swap. It consists of many swap_map_page structures
+ * that contain each an array of MAP_PAGE_SIZE swap entries.
+ * These structures are linked together with the help of either the
+ * .next (in memory) or the .next_swap (in swap) member.
+ *
+ * The swap map is created during suspend. At that time we need to keep
+ * it in memory, because we have to free all of the allocated swap
+ * entries if an error occurs. The memory needed is preallocated
+ * so that we know in advance if there's enough of it.
+ *
+ * The first swap_map_page structure is filled with the swap entries that
+ * correspond to the first MAP_PAGE_SIZE data pages written to swap and
+ * so on. After the all of the data pages have been written, the order
+ * of the swap_map_page structures in the map is reversed so that they
+ * can be read from swap in the original order. This causes the data
+ * pages to be loaded in exactly the same order in which they have been
+ * saved.
+ *
+ * During resume we only need to use one swap_map_page structure
+ * at a time, which means that we only need to use two memory pages for
+ * reading the image - one for reading the swap_map_page structures
+ * and the second for reading the data pages from swap.
+ */
+
+#define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \
+ / sizeof(swp_entry_t))
+
+struct swap_map_page {
+ swp_entry_t entries[MAP_PAGE_SIZE];
+ swp_entry_t next_swap;
+ struct swap_map_page *next;
+};
+
+static inline void free_swap_map(struct swap_map_page *swap_map)
+{
+ struct swap_map_page *swp;
+
+ while (swap_map) {
+ swp = swap_map->next;
+ free_page((unsigned long)swap_map);
+ swap_map = swp;
+ }
+}
+
+static struct swap_map_page *alloc_swap_map(unsigned int nr_pages)
+{
+ struct swap_map_page *swap_map, *swp;
+ unsigned n = 0;
+
+ if (!nr_pages)
+ return NULL;
+
+ pr_debug("alloc_swap_map(): nr_pages = %d\n", nr_pages);
+ swap_map = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
+ swp = swap_map;
+ for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) {
+ swp->next = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
+ swp = swp->next;
+ if (!swp) {
+ free_swap_map(swap_map);
+ return NULL;
+ }
+ }
+ return swap_map;
+}
+
+/**
+ * reverse_swap_map - reverse the order of pages in the swap map
+ * @swap_map
+ */
+
+static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map)
+{
+ struct swap_map_page *prev, *next;
+
+ prev = NULL;
+ while (swap_map) {
+ next = swap_map->next;
+ swap_map->next = prev;
+ prev = swap_map;
+ swap_map = next;
+ }
+ return prev;
+}
+
+/**
+ * free_swap_map_entries - free the swap entries allocated to store
+ * the swap map @swap_map (this is only called in case of an error)
+ */
+static inline void free_swap_map_entries(struct swap_map_page *swap_map)
+{
+ while (swap_map) {
+ if (swap_map->next_swap.val)
+ swap_free(swap_map->next_swap);
+ swap_map = swap_map->next;
+ }
+}
+
+/**
+ * save_swap_map - save the swap map used for tracing the data pages
+ * stored in the swap
+ */
+
+static int save_swap_map(struct swap_map_page *swap_map, swp_entry_t *start)
+{
+ swp_entry_t entry = (swp_entry_t){0};
+ int error;
+
+ while (swap_map) {
+ swap_map->next_swap = entry;
+ if ((error = write_page(swap_map, &entry)))
+ return error;
+ swap_map = swap_map->next;
+ }
+ *start = entry;
+ return 0;
+}
+
+/**
+ * free_image_entries - free the swap entries allocated to store
+ * the image data pages (this is only called in case of an error)
+ */
+
+static inline void free_image_entries(struct swap_map_page *swp)
+{
+ unsigned k;
+
+ while (swp) {
+ for (k = 0; k < MAP_PAGE_SIZE; k++)
+ if (swp->entries[k].val)
+ swap_free(swp->entries[k]);
+ swp = swp->next;
+ }
+}
+
+/**
+ * The swap_map_handle structure is used for handling the swap map in
+ * a file-alike way
+ */
+
+struct swap_map_handle {
+ struct swap_map_page *cur;
+ unsigned int k;
+};
+
+static inline void init_swap_map_handle(struct swap_map_handle *handle,
+ struct swap_map_page *map)
+{
+ handle->cur = map;
+ handle->k = 0;
+}
+
+static inline int swap_map_write_page(struct swap_map_handle *handle, void *buf)
+{
+ int error;
+
+ error = write_page(buf, handle->cur->entries + handle->k);
+ if (error)
+ return error;
+ if (++handle->k >= MAP_PAGE_SIZE) {
+ handle->cur = handle->cur->next;
+ handle->k = 0;
+ }
+ return 0;
+}
+
+/**
+ * save_image - save the suspend image data
+ */
+
+static int save_image(struct swap_map_handle *handle,
+ struct snapshot_handle *snapshot,
+ unsigned int nr_pages)
+{
+ unsigned int m;
+ int ret;
+ int error = 0;
+
+ printk("Saving image data pages (%u pages) ... ", nr_pages);
+ m = nr_pages / 100;
+ if (!m)
+ m = 1;
+ nr_pages = 0;
+ do {
+ ret = snapshot_read_next(snapshot, PAGE_SIZE);
+ if (ret > 0) {
+ error = swap_map_write_page(handle, data_of(*snapshot));
+ if (error)
+ break;
+ if (!(nr_pages % m))
+ printk("\b\b\b\b%3d%%", nr_pages / m);
+ nr_pages++;
+ }
+ } while (ret > 0);
+ if (!error)
+ printk("\b\b\b\bdone\n");
+ return error;
+}
+
+/**
+ * enough_swap - Make sure we have enough swap to save the image.
+ *
+ * Returns TRUE or FALSE after checking the total amount of swap
+ * space avaiable from the resume partition.
+ */
+
+static int enough_swap(unsigned int nr_pages)
+{
+ unsigned int free_swap = swsusp_available_swap(root_swap);
+
+ pr_debug("swsusp: free swap pages: %u\n", free_swap);
+ return free_swap > (nr_pages + PAGES_FOR_IO +
+ (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
+}
+
+/**
+ * swsusp_write - Write entire image and metadata.
+ *
+ * It is important _NOT_ to umount filesystems at this point. We want
+ * them synced (in case something goes wrong) but we DO not want to mark
+ * filesystem clean: it is not. (And it does not matter, if we resume
+ * correctly, we'll mark system clean, anyway.)
+ */
+
+int swsusp_write(void)
+{
+ struct swap_map_page *swap_map;
+ struct swap_map_handle handle;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
+ unsigned int nr_pages;
+ swp_entry_t start;
+ int error;
+
+ if ((error = swsusp_swap_check())) {
+ printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n");
+ return error;
+ }
+ memset(&snapshot, 0, sizeof(struct snapshot_handle));
+ error = snapshot_read_next(&snapshot, PAGE_SIZE);
+ if (error < PAGE_SIZE)
+ return error < 0 ? error : -EFAULT;
+ header = (struct swsusp_info *)data_of(snapshot);
+ nr_pages = header->image_pages;
+ if (!enough_swap(nr_pages)) {
+ printk(KERN_ERR "swsusp: Not enough free swap\n");
+ return -ENOSPC;
+ }
+ swap_map = alloc_swap_map(header->pages);
+ if (!swap_map)
+ return -ENOMEM;
+ init_swap_map_handle(&handle, swap_map);
+
+ error = swap_map_write_page(&handle, header);
+ if (!error)
+ error = save_image(&handle, &snapshot, nr_pages);
+ if (error)
+ goto Free_image_entries;
+
+ swap_map = reverse_swap_map(swap_map);
+ error = save_swap_map(swap_map, &start);
+ if (error)
+ goto Free_map_entries;
+
+ printk( "S" );
+ error = mark_swapfiles(start);
+ printk( "|\n" );
+ if (error)
+ goto Free_map_entries;
+
+Free_swap_map:
+ free_swap_map(swap_map);
+ return error;
+
+Free_map_entries:
+ free_swap_map_entries(swap_map);
+Free_image_entries:
+ free_image_entries(swap_map);
+ goto Free_swap_map;
+}
+
+/*
+ * Using bio to read from swap.
+ * This code requires a bit more work than just using buffer heads
+ * but, it is the recommended way for 2.5/2.6.
+ * The following are to signal the beginning and end of I/O. Bios
+ * finish asynchronously, while we want them to happen synchronously.
+ * A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+static int end_io(struct bio *bio, unsigned int num, int err)
+{
+ if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
+ panic("I/O error reading memory image");
+ atomic_set(&io_done, 0);
+ return 0;
+}
+
+static struct block_device *resume_bdev;
+
+/**
+ * submit - submit BIO request.
+ * @rw: READ or WRITE.
+ * @off physical offset of page.
+ * @page: page we're reading or writing.
+ *
+ * Straight from the textbook - allocate and initialize the bio.
+ * If we're writing, make sure the page is marked as dirty.
+ * Then submit it and wait.
+ */
+
+static int submit(int rw, pgoff_t page_off, void *page)
+{
+ int error = 0;
+ struct bio *bio;
+
+ bio = bio_alloc(GFP_ATOMIC, 1);
+ if (!bio)
+ return -ENOMEM;
+ bio->bi_sector = page_off * (PAGE_SIZE >> 9);
+ bio_get(bio);
+ bio->bi_bdev = resume_bdev;
+ bio->bi_end_io = end_io;
+
+ if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+ printk("swsusp: ERROR: adding page to bio at %ld\n",page_off);
+ error = -EFAULT;
+ goto Done;
+ }
+
+ if (rw == WRITE)
+ bio_set_pages_dirty(bio);
+
+ atomic_set(&io_done, 1);
+ submit_bio(rw | (1 << BIO_RW_SYNC), bio);
+ while (atomic_read(&io_done))
+ yield();
+
+ Done:
+ bio_put(bio);
+ return error;
+}
+
+static int bio_read_page(pgoff_t page_off, void *page)
+{
+ return submit(READ, page_off, page);
+}
+
+static int bio_write_page(pgoff_t page_off, void *page)
+{
+ return submit(WRITE, page_off, page);
+}
+
+/**
+ * The following functions allow us to read data using a swap map
+ * in a file-alike way
+ */
+
+static inline void release_swap_map_reader(struct swap_map_handle *handle)
+{
+ if (handle->cur)
+ free_page((unsigned long)handle->cur);
+ handle->cur = NULL;
+}
+
+static inline int get_swap_map_reader(struct swap_map_handle *handle,
+ swp_entry_t start)
+{
+ int error;
+
+ if (!swp_offset(start))
+ return -EINVAL;
+ handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
+ if (!handle->cur)
+ return -ENOMEM;
+ error = bio_read_page(swp_offset(start), handle->cur);
+ if (error) {
+ release_swap_map_reader(handle);
+ return error;
+ }
+ handle->k = 0;
+ return 0;
+}
+
+static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf)
+{
+ unsigned long offset;
+ int error;
+
+ if (!handle->cur)
+ return -EINVAL;
+ offset = swp_offset(handle->cur->entries[handle->k]);
+ if (!offset)
+ return -EFAULT;
+ error = bio_read_page(offset, buf);
+ if (error)
+ return error;
+ if (++handle->k >= MAP_PAGE_SIZE) {
+ handle->k = 0;
+ offset = swp_offset(handle->cur->next_swap);
+ if (!offset)
+ release_swap_map_reader(handle);
+ else
+ error = bio_read_page(offset, handle->cur);
+ }
+ return error;
+}
+
+/**
+ * load_image - load the image using the swap map handle
+ * @handle and the snapshot handle @snapshot
+ * (assume there are @nr_pages pages to load)
+ */
+
+static int load_image(struct swap_map_handle *handle,
+ struct snapshot_handle *snapshot,
+ unsigned int nr_pages)
+{
+ unsigned int m;
+ int ret;
+ int error = 0;
+
+ printk("Loading image data pages (%u pages) ... ", nr_pages);
+ m = nr_pages / 100;
+ if (!m)
+ m = 1;
+ nr_pages = 0;
+ do {
+ ret = snapshot_write_next(snapshot, PAGE_SIZE);
+ if (ret > 0) {
+ error = swap_map_read_page(handle, data_of(*snapshot));
+ if (error)
+ break;
+ if (!(nr_pages % m))
+ printk("\b\b\b\b%3d%%", nr_pages / m);
+ nr_pages++;
+ }
+ } while (ret > 0);
+ if (!error)
+ printk("\b\b\b\bdone\n");
+ if (!snapshot_image_ready(snapshot))
+ error = -ENODATA;
+ return error;
+}
+
+int swsusp_read(void)
+{
+ int error;
+ struct swap_map_handle handle;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
+ unsigned int nr_pages;
+
+ if (IS_ERR(resume_bdev)) {
+ pr_debug("swsusp: block device not initialised\n");
+ return PTR_ERR(resume_bdev);
+ }
+
+ memset(&snapshot, 0, sizeof(struct snapshot_handle));
+ error = snapshot_write_next(&snapshot, PAGE_SIZE);
+ if (error < PAGE_SIZE)
+ return error < 0 ? error : -EFAULT;
+ header = (struct swsusp_info *)data_of(snapshot);
+ error = get_swap_map_reader(&handle, swsusp_header.image);
+ if (!error)
+ error = swap_map_read_page(&handle, header);
+ if (!error) {
+ nr_pages = header->image_pages;
+ error = load_image(&handle, &snapshot, nr_pages);
+ }
+ release_swap_map_reader(&handle);
+
+ blkdev_put(resume_bdev);
+
+ if (!error)
+ pr_debug("swsusp: Reading resume file was successful\n");
+ else
+ pr_debug("swsusp: Error %d resuming\n", error);
+ return error;
+}
+
+/**
+ * swsusp_check - Check for swsusp signature in the resume device
+ */
+
+int swsusp_check(void)
+{
+ int error;
+
+ resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
+ if (!IS_ERR(resume_bdev)) {
+ set_blocksize(resume_bdev, PAGE_SIZE);
+ memset(&swsusp_header, 0, sizeof(swsusp_header));
+ if ((error = bio_read_page(0, &swsusp_header)))
+ return error;
+ if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
+ memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
+ /* Reset swap signature now */
+ error = bio_write_page(0, &swsusp_header);
+ } else {
+ return -EINVAL;
+ }
+ if (error)
+ blkdev_put(resume_bdev);
+ else
+ pr_debug("swsusp: Signature found, resuming\n");
+ } else {
+ error = PTR_ERR(resume_bdev);
+ }
+
+ if (error)
+ pr_debug("swsusp: Error %d check for resume file\n", error);
+
+ return error;
+}
+
+/**
+ * swsusp_close - close swap device.
+ */
+
+void swsusp_close(void)
+{
+ if (IS_ERR(resume_bdev)) {
+ pr_debug("swsusp: block device not initialised\n");
+ return;
+ }
+
+ blkdev_put(resume_bdev);
+}
Index: linux-2.6.15-rc5-mm3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/swsusp.c 2005-12-27 21:53:03.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2005-12-27 23:42:40.000000000 +0100
@@ -38,34 +38,17 @@
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
*/
-#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
-#include <linux/smp_lock.h>
-#include <linux/file.h>
-#include <linux/utsname.h>
-#include <linux/version.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
#include <linux/spinlock.h>
-#include <linux/genhd.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/swap.h>
#include <linux/pm.h>
-#include <linux/device.h>
-#include <linux/buffer_head.h>
#include <linux/swapops.h>
#include <linux/bootmem.h>
#include <linux/syscalls.h>
#include <linux/highmem.h>
-#include <linux/bio.h>
-
-#include <asm/uaccess.h>
-#include <asm/mmu_context.h>
-#include <asm/pgtable.h>
-#include <asm/tlbflush.h>
-#include <asm/io.h>
#include "power.h"
@@ -89,47 +72,7 @@ static int restore_highmem(void) { retur
static unsigned int count_highmem_pages(void) { return 0; }
#endif
-extern char resume_file[];
-
-#define SWSUSP_SIG "S1SUSPEND"
-
-static struct swsusp_header {
- char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)];
- swp_entry_t image;
- char orig_sig[10];
- char sig[10];
-} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
-
-/*
- * Saving part...
- */
-
-static unsigned short root_swap = 0xffff;
-
-static int mark_swapfiles(swp_entry_t start)
-{
- int error;
-
- rw_swap_page_sync(READ,
- swp_entry(root_swap, 0),
- virt_to_page((unsigned long)&swsusp_header));
- if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) ||
- !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
- memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
- memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
- swsusp_header.image = start;
- error = rw_swap_page_sync(WRITE,
- swp_entry(root_swap, 0),
- virt_to_page((unsigned long)
- &swsusp_header));
- } else {
- pr_debug("swsusp: Partition is not swap space.\n");
- error = -ENODEV;
- }
- return error;
-}
-
-/*
+/**
* Check whether the swap device is the specified resume
* device, irrespective of whether they are specified by
* identical names.
@@ -140,6 +83,7 @@ static int mark_swapfiles(swp_entry_t st
* devfs, since the resume code can only recognize the form /dev/hda4,
* but the suspend code would see the long name.)
*/
+
static inline int is_resume_device(const struct swap_info_struct *swap_info)
{
struct file *file = swap_info->swap_file;
@@ -168,242 +112,6 @@ int swsusp_get_swap_index(void)
return -ENODEV;
}
-static int swsusp_swap_check(void) /* This is called before saving image */
-{
- int res = swsusp_get_swap_index();
-
- if (res >= 0) {
- root_swap = res;
- return 0;
- }
- return res;
-}
-
-/**
- * write_page - Write one page to a fresh swap location.
- * @buf: Address we're writing.
- * @loc: Place to store the entry we used.
- */
-
-static int write_page(void *buf, swp_entry_t *loc)
-{
- swp_entry_t entry;
- int error = -ENOSPC;
-
- entry = get_swap_page_of_type(root_swap);
- if (swp_offset(entry)) {
- error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
- if (!error)
- *loc = entry;
- else
- swap_free(entry);
- }
- return error;
-}
-
-/**
- * Swap map-handling functions
- *
- * The swap map is a data structure used for keeping track of each page
- * written to the swap. It consists of many swap_map_page structures
- * that contain each an array of MAP_PAGE_SIZE swap entries.
- * These structures are linked together with the help of either the
- * .next (in memory) or the .next_swap (in swap) member.
- *
- * The swap map is created during suspend. At that time we need to keep
- * it in memory, because we have to free all of the allocated swap
- * entries if an error occurs. The memory needed is preallocated
- * so that we know in advance if there's enough of it.
- *
- * The first swap_map_page structure is filled with the swap entries that
- * correspond to the first MAP_PAGE_SIZE data pages written to swap and
- * so on. After the all of the data pages have been written, the order
- * of the swap_map_page structures in the map is reversed so that they
- * can be read from swap in the original order. This causes the data
- * pages to be loaded in exactly the same order in which they have been
- * saved.
- *
- * During resume we only need to use one swap_map_page structure
- * at a time, which means that we only need to use two memory pages for
- * reading the image - one for reading the swap_map_page structures
- * and the second for reading the data pages from swap.
- */
-
-#define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \
- / sizeof(swp_entry_t))
-
-struct swap_map_page {
- swp_entry_t entries[MAP_PAGE_SIZE];
- swp_entry_t next_swap;
- struct swap_map_page *next;
-};
-
-static inline void free_swap_map(struct swap_map_page *swap_map)
-{
- struct swap_map_page *swp;
-
- while (swap_map) {
- swp = swap_map->next;
- free_page((unsigned long)swap_map);
- swap_map = swp;
- }
-}
-
-static struct swap_map_page *alloc_swap_map(unsigned int nr_pages)
-{
- struct swap_map_page *swap_map, *swp;
- unsigned n = 0;
-
- if (!nr_pages)
- return NULL;
-
- pr_debug("alloc_swap_map(): nr_pages = %d\n", nr_pages);
- swap_map = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
- swp = swap_map;
- for (n = MAP_PAGE_SIZE; n < nr_pages; n += MAP_PAGE_SIZE) {
- swp->next = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
- swp = swp->next;
- if (!swp) {
- free_swap_map(swap_map);
- return NULL;
- }
- }
- return swap_map;
-}
-
-/**
- * reverse_swap_map - reverse the order of pages in the swap map
- * @swap_map
- */
-
-static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map)
-{
- struct swap_map_page *prev, *next;
-
- prev = NULL;
- while (swap_map) {
- next = swap_map->next;
- swap_map->next = prev;
- prev = swap_map;
- swap_map = next;
- }
- return prev;
-}
-
-/**
- * free_swap_map_entries - free the swap entries allocated to store
- * the swap map @swap_map (this is only called in case of an error)
- */
-static inline void free_swap_map_entries(struct swap_map_page *swap_map)
-{
- while (swap_map) {
- if (swap_map->next_swap.val)
- swap_free(swap_map->next_swap);
- swap_map = swap_map->next;
- }
-}
-
-/**
- * save_swap_map - save the swap map used for tracing the data pages
- * stored in the swap
- */
-
-static int save_swap_map(struct swap_map_page *swap_map, swp_entry_t *start)
-{
- swp_entry_t entry = (swp_entry_t){0};
- int error;
-
- while (swap_map) {
- swap_map->next_swap = entry;
- if ((error = write_page(swap_map, &entry)))
- return error;
- swap_map = swap_map->next;
- }
- *start = entry;
- return 0;
-}
-
-/**
- * free_image_entries - free the swap entries allocated to store
- * the image data pages (this is only called in case of an error)
- */
-
-static inline void free_image_entries(struct swap_map_page *swp)
-{
- unsigned k;
-
- while (swp) {
- for (k = 0; k < MAP_PAGE_SIZE; k++)
- if (swp->entries[k].val)
- swap_free(swp->entries[k]);
- swp = swp->next;
- }
-}
-
-/**
- * The swap_map_handle structure is used for handling the swap map in
- * a file-alike way
- */
-
-struct swap_map_handle {
- struct swap_map_page *cur;
- unsigned int k;
-};
-
-static inline void init_swap_map_handle(struct swap_map_handle *handle,
- struct swap_map_page *map)
-{
- handle->cur = map;
- handle->k = 0;
-}
-
-static inline int swap_map_write_page(struct swap_map_handle *handle, void *buf)
-{
- int error;
-
- error = write_page(buf, handle->cur->entries + handle->k);
- if (error)
- return error;
- if (++handle->k >= MAP_PAGE_SIZE) {
- handle->cur = handle->cur->next;
- handle->k = 0;
- }
- return 0;
-}
-
-/**
- * save_image - save the suspend image data
- */
-
-static int save_image(struct swap_map_handle *handle,
- struct snapshot_handle *snapshot,
- unsigned int nr_pages)
-{
- unsigned int m;
- int ret;
- int error = 0;
-
- printk("Saving image data pages (%u pages) ... ", nr_pages);
- m = nr_pages / 100;
- if (!m)
- m = 1;
- nr_pages = 0;
- do {
- ret = snapshot_read_next(snapshot, PAGE_SIZE);
- if (ret > 0) {
- error = swap_map_write_page(handle, data_of(*snapshot));
- if (error)
- break;
- if (!(nr_pages % m))
- printk("\b\b\b\b%3d%%", nr_pages / m);
- nr_pages++;
- }
- } while (ret > 0);
- if (!error)
- printk("\b\b\b\bdone\n");
- return error;
-}
-
/**
* swsusp_available_swap - check the total amount of swap
* space avaiable from given swap partition/file
@@ -424,88 +132,6 @@ unsigned int swsusp_available_swap(unsig
}
/**
- * enough_swap - Make sure we have enough swap to save the image.
- *
- * Returns TRUE or FALSE after checking the total amount of swap
- * space avaiable from the resume partition.
- */
-
-static int enough_swap(unsigned int nr_pages)
-{
- unsigned int free_swap = swsusp_available_swap(root_swap);
-
- pr_debug("swsusp: free swap pages: %u\n", free_swap);
- return free_swap > (nr_pages + PAGES_FOR_IO +
- (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE);
-}
-
-/**
- * swsusp_write - Write entire image and metadata.
- *
- * It is important _NOT_ to umount filesystems at this point. We want
- * them synced (in case something goes wrong) but we DO not want to mark
- * filesystem clean: it is not. (And it does not matter, if we resume
- * correctly, we'll mark system clean, anyway.)
- */
-
-int swsusp_write(void)
-{
- struct swap_map_page *swap_map;
- struct swap_map_handle handle;
- struct snapshot_handle snapshot;
- struct swsusp_info *header;
- unsigned int nr_pages;
- swp_entry_t start;
- int error;
-
- if ((error = swsusp_swap_check())) {
- printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n");
- return error;
- }
- memset(&snapshot, 0, sizeof(struct snapshot_handle));
- error = snapshot_read_next(&snapshot, PAGE_SIZE);
- if (error < PAGE_SIZE)
- return error < 0 ? error : -EFAULT;
- header = (struct swsusp_info *)data_of(snapshot);
- nr_pages = header->image_pages;
- if (!enough_swap(nr_pages)) {
- printk(KERN_ERR "swsusp: Not enough free swap\n");
- return -ENOSPC;
- }
- swap_map = alloc_swap_map(header->pages);
- if (!swap_map)
- return -ENOMEM;
- init_swap_map_handle(&handle, swap_map);
-
- error = swap_map_write_page(&handle, header);
- if (!error)
- error = save_image(&handle, &snapshot, nr_pages);
- if (error)
- goto Free_image_entries;
-
- swap_map = reverse_swap_map(swap_map);
- error = save_swap_map(swap_map, &start);
- if (error)
- goto Free_map_entries;
-
- printk( "S" );
- error = mark_swapfiles(start);
- printk( "|\n" );
- if (error)
- goto Free_map_entries;
-
-Free_swap_map:
- free_swap_map(swap_map);
- return error;
-
-Free_map_entries:
- free_swap_map_entries(swap_map);
-Free_image_entries:
- free_image_entries(swap_map);
- goto Free_swap_map;
-}
-
-/**
* swsusp_shrink_memory - Try to free as much memory as needed
*
* ... but do not OOM-kill anyone
@@ -611,254 +237,3 @@ int swsusp_resume(void)
local_irq_enable();
return error;
}
-
-/*
- * Using bio to read from swap.
- * This code requires a bit more work than just using buffer heads
- * but, it is the recommended way for 2.5/2.6.
- * The following are to signal the beginning and end of I/O. Bios
- * finish asynchronously, while we want them to happen synchronously.
- * A simple atomic_t, and a wait loop take care of this problem.
- */
-
-static atomic_t io_done = ATOMIC_INIT(0);
-
-static int end_io(struct bio *bio, unsigned int num, int err)
-{
- if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
- panic("I/O error reading memory image");
- atomic_set(&io_done, 0);
- return 0;
-}
-
-static struct block_device *resume_bdev;
-
-/**
- * submit - submit BIO request.
- * @rw: READ or WRITE.
- * @off physical offset of page.
- * @page: page we're reading or writing.
- *
- * Straight from the textbook - allocate and initialize the bio.
- * If we're writing, make sure the page is marked as dirty.
- * Then submit it and wait.
- */
-
-static int submit(int rw, pgoff_t page_off, void *page)
-{
- int error = 0;
- struct bio *bio;
-
- bio = bio_alloc(GFP_ATOMIC, 1);
- if (!bio)
- return -ENOMEM;
- bio->bi_sector = page_off * (PAGE_SIZE >> 9);
- bio_get(bio);
- bio->bi_bdev = resume_bdev;
- bio->bi_end_io = end_io;
-
- if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
- printk("swsusp: ERROR: adding page to bio at %ld\n",page_off);
- error = -EFAULT;
- goto Done;
- }
-
- if (rw == WRITE)
- bio_set_pages_dirty(bio);
-
- atomic_set(&io_done, 1);
- submit_bio(rw | (1 << BIO_RW_SYNC), bio);
- while (atomic_read(&io_done))
- yield();
-
- Done:
- bio_put(bio);
- return error;
-}
-
-static int bio_read_page(pgoff_t page_off, void *page)
-{
- return submit(READ, page_off, page);
-}
-
-static int bio_write_page(pgoff_t page_off, void *page)
-{
- return submit(WRITE, page_off, page);
-}
-
-/**
- * The following functions allow us to read data using a swap map
- * in a file-alike way
- */
-
-static inline void release_swap_map_reader(struct swap_map_handle *handle)
-{
- if (handle->cur)
- free_page((unsigned long)handle->cur);
- handle->cur = NULL;
-}
-
-static inline int get_swap_map_reader(struct swap_map_handle *handle,
- swp_entry_t start)
-{
- int error;
-
- if (!swp_offset(start))
- return -EINVAL;
- handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_ATOMIC);
- if (!handle->cur)
- return -ENOMEM;
- error = bio_read_page(swp_offset(start), handle->cur);
- if (error) {
- release_swap_map_reader(handle);
- return error;
- }
- handle->k = 0;
- return 0;
-}
-
-static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf)
-{
- unsigned long offset;
- int error;
-
- if (!handle->cur)
- return -EINVAL;
- offset = swp_offset(handle->cur->entries[handle->k]);
- if (!offset)
- return -EFAULT;
- error = bio_read_page(offset, buf);
- if (error)
- return error;
- if (++handle->k >= MAP_PAGE_SIZE) {
- handle->k = 0;
- offset = swp_offset(handle->cur->next_swap);
- if (!offset)
- release_swap_map_reader(handle);
- else
- error = bio_read_page(offset, handle->cur);
- }
- return error;
-}
-
-/**
- * load_image - load the image using the swap map handle
- * @handle and the snapshot handle @snapshot
- * (assume there are @nr_pages pages to load)
- */
-
-static int load_image(struct swap_map_handle *handle,
- struct snapshot_handle *snapshot,
- unsigned int nr_pages)
-{
- unsigned int m;
- int ret;
- int error = 0;
-
- printk("Loading image data pages (%u pages) ... ", nr_pages);
- m = nr_pages / 100;
- if (!m)
- m = 1;
- nr_pages = 0;
- do {
- ret = snapshot_write_next(snapshot, PAGE_SIZE);
- if (ret > 0) {
- error = swap_map_read_page(handle, data_of(*snapshot));
- if (error)
- break;
- if (!(nr_pages % m))
- printk("\b\b\b\b%3d%%", nr_pages / m);
- nr_pages++;
- }
- } while (ret > 0);
- if (!error)
- printk("\b\b\b\bdone\n");
- if (!snapshot_image_ready(snapshot))
- error = -ENODATA;
- return error;
-}
-
-int swsusp_read(void)
-{
- int error;
- struct swap_map_handle handle;
- struct snapshot_handle snapshot;
- struct swsusp_info *header;
- unsigned int nr_pages;
-
- if (IS_ERR(resume_bdev)) {
- pr_debug("swsusp: block device not initialised\n");
- return PTR_ERR(resume_bdev);
- }
-
- memset(&snapshot, 0, sizeof(struct snapshot_handle));
- error = snapshot_write_next(&snapshot, PAGE_SIZE);
- if (error < PAGE_SIZE)
- return error < 0 ? error : -EFAULT;
- header = (struct swsusp_info *)data_of(snapshot);
- error = get_swap_map_reader(&handle, swsusp_header.image);
- if (!error)
- error = swap_map_read_page(&handle, header);
- if (!error) {
- nr_pages = header->image_pages;
- error = load_image(&handle, &snapshot, nr_pages);
- }
- release_swap_map_reader(&handle);
-
- blkdev_put(resume_bdev);
-
- if (!error)
- pr_debug("swsusp: Reading resume file was successful\n");
- else
- pr_debug("swsusp: Error %d resuming\n", error);
- return error;
-}
-
-/**
- * swsusp_check - Check for swsusp signature in the resume device
- */
-
-int swsusp_check(void)
-{
- int error;
-
- resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
- if (!IS_ERR(resume_bdev)) {
- set_blocksize(resume_bdev, PAGE_SIZE);
- memset(&swsusp_header, 0, sizeof(swsusp_header));
- if ((error = bio_read_page(0, &swsusp_header)))
- return error;
- if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
- memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
- /* Reset swap signature now */
- error = bio_write_page(0, &swsusp_header);
- } else {
- return -EINVAL;
- }
- if (error)
- blkdev_put(resume_bdev);
- else
- pr_debug("swsusp: Signature found, resuming\n");
- } else {
- error = PTR_ERR(resume_bdev);
- }
-
- if (error)
- pr_debug("swsusp: Error %d check for resume file\n", error);
-
- return error;
-}
-
-/**
- * swsusp_close - close swap device.
- */
-
-void swsusp_close(void)
-{
- if (IS_ERR(resume_bdev)) {
- pr_debug("swsusp: block device not initialised\n");
- return;
- }
-
- blkdev_put(resume_bdev);
-}
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC/RFT][PATCH -mm 4/4] swsusp: move highmem-handling code to swsusp.c
2005-12-28 0:08 [RFC/RFT][PATCH -mm 0/4] swsusp: userland interface Rafael J. Wysocki
` (2 preceding siblings ...)
2005-12-28 0:28 ` [RFC/RFT][PATCH -mm 3/4] swsusp: separate swap-writing and reading code Rafael J. Wysocki
@ 2005-12-28 0:36 ` Rafael J. Wysocki
3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2005-12-28 0:36 UTC (permalink / raw)
To: Linux PM; +Cc: LKML, Pavel Machek
This patch moves all of the highmem-related functions from snapshot.c to
swsusp.c.
These functions are only called by the code in swsusp.c and are not related
to the snapshot-handling code, so they can be moved from snapshot.c,
especially that swsusp.c is much smaller after the swap-writing/reading
code has been moved to another file.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
kernel/power/snapshot.c | 113 ------------------------------------------------
kernel/power/swsusp.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 110 insertions(+), 116 deletions(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/snapshot.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/snapshot.c 2005-12-28 01:16:43.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/snapshot.c 2005-12-28 01:18:06.000000000 +0100
@@ -38,119 +38,6 @@ struct pbe *pagedir_nosave;
unsigned int nr_copy_pages;
unsigned int nr_meta_pages;
-#ifdef CONFIG_HIGHMEM
-unsigned int count_highmem_pages(void)
-{
- struct zone *zone;
- unsigned long zone_pfn;
- unsigned int n = 0;
-
- for_each_zone (zone)
- if (is_highmem(zone)) {
- mark_free_pages(zone);
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) {
- struct page *page;
- unsigned long pfn = zone_pfn + zone->zone_start_pfn;
- if (!pfn_valid(pfn))
- continue;
- page = pfn_to_page(pfn);
- if (PageReserved(page))
- continue;
- if (PageNosaveFree(page))
- continue;
- n++;
- }
- }
- return n;
-}
-
-struct highmem_page {
- char *data;
- struct page *page;
- struct highmem_page *next;
-};
-
-static struct highmem_page *highmem_copy;
-
-static int save_highmem_zone(struct zone *zone)
-{
- unsigned long zone_pfn;
- mark_free_pages(zone);
- for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {
- struct page *page;
- struct highmem_page *save;
- void *kaddr;
- unsigned long pfn = zone_pfn + zone->zone_start_pfn;
-
- if (!(pfn%1000))
- printk(".");
- if (!pfn_valid(pfn))
- continue;
- page = pfn_to_page(pfn);
- /*
- * This condition results from rvmalloc() sans vmalloc_32()
- * and architectural memory reservations. This should be
- * corrected eventually when the cases giving rise to this
- * are better understood.
- */
- if (PageReserved(page)) {
- printk("highmem reserved page?!\n");
- continue;
- }
- BUG_ON(PageNosave(page));
- if (PageNosaveFree(page))
- continue;
- save = kmalloc(sizeof(struct highmem_page), GFP_ATOMIC);
- if (!save)
- return -ENOMEM;
- save->next = highmem_copy;
- save->page = page;
- save->data = (void *) get_zeroed_page(GFP_ATOMIC);
- if (!save->data) {
- kfree(save);
- return -ENOMEM;
- }
- kaddr = kmap_atomic(page, KM_USER0);
- memcpy(save->data, kaddr, PAGE_SIZE);
- kunmap_atomic(kaddr, KM_USER0);
- highmem_copy = save;
- }
- return 0;
-}
-
-int save_highmem(void)
-{
- struct zone *zone;
- int res = 0;
-
- pr_debug("swsusp: Saving Highmem\n");
- for_each_zone (zone) {
- if (is_highmem(zone))
- res = save_highmem_zone(zone);
- if (res)
- return res;
- }
- return 0;
-}
-
-int restore_highmem(void)
-{
- printk("swsusp: Restoring Highmem\n");
- while (highmem_copy) {
- struct highmem_page *save = highmem_copy;
- void *kaddr;
- highmem_copy = save->next;
-
- kaddr = kmap_atomic(save->page, KM_USER0);
- memcpy(kaddr, save->data, PAGE_SIZE);
- kunmap_atomic(kaddr, KM_USER0);
- free_page((long) save->data);
- kfree(save);
- }
- return 0;
-}
-#endif
-
static int pfn_is_nosave(unsigned long pfn)
{
unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
Index: linux-2.6.15-rc5-mm3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/swsusp.c 2005-12-28 01:18:04.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2005-12-28 01:34:59.000000000 +0100
@@ -63,9 +63,116 @@ unsigned int image_size = 500;
int in_suspend __nosavedata = 0;
#ifdef CONFIG_HIGHMEM
-unsigned int count_highmem_pages(void);
-int save_highmem(void);
-int restore_highmem(void);
+static unsigned int count_highmem_pages(void)
+{
+ struct zone *zone;
+ unsigned long zone_pfn;
+ unsigned int n = 0;
+
+ for_each_zone (zone)
+ if (is_highmem(zone)) {
+ mark_free_pages(zone);
+ for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) {
+ struct page *page;
+ unsigned long pfn = zone_pfn + zone->zone_start_pfn;
+ if (!pfn_valid(pfn))
+ continue;
+ page = pfn_to_page(pfn);
+ if (PageReserved(page))
+ continue;
+ if (PageNosaveFree(page))
+ continue;
+ n++;
+ }
+ }
+ return n;
+}
+
+struct highmem_page {
+ char *data;
+ struct page *page;
+ struct highmem_page *next;
+};
+
+static struct highmem_page *highmem_copy;
+
+static int save_highmem_zone(struct zone *zone)
+{
+ unsigned long zone_pfn;
+ mark_free_pages(zone);
+ for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) {
+ struct page *page;
+ struct highmem_page *save;
+ void *kaddr;
+ unsigned long pfn = zone_pfn + zone->zone_start_pfn;
+
+ if (!(pfn%1000))
+ printk(".");
+ if (!pfn_valid(pfn))
+ continue;
+ page = pfn_to_page(pfn);
+ /*
+ * This condition results from rvmalloc() sans vmalloc_32()
+ * and architectural memory reservations. This should be
+ * corrected eventually when the cases giving rise to this
+ * are better understood.
+ */
+ if (PageReserved(page)) {
+ printk("highmem reserved page?!\n");
+ continue;
+ }
+ BUG_ON(PageNosave(page));
+ if (PageNosaveFree(page))
+ continue;
+ save = kmalloc(sizeof(struct highmem_page), GFP_ATOMIC);
+ if (!save)
+ return -ENOMEM;
+ save->next = highmem_copy;
+ save->page = page;
+ save->data = (void *) get_zeroed_page(GFP_ATOMIC);
+ if (!save->data) {
+ kfree(save);
+ return -ENOMEM;
+ }
+ kaddr = kmap_atomic(page, KM_USER0);
+ memcpy(save->data, kaddr, PAGE_SIZE);
+ kunmap_atomic(kaddr, KM_USER0);
+ highmem_copy = save;
+ }
+ return 0;
+}
+
+static int save_highmem(void)
+{
+ struct zone *zone;
+ int res = 0;
+
+ pr_debug("swsusp: Saving Highmem\n");
+ for_each_zone (zone) {
+ if (is_highmem(zone))
+ res = save_highmem_zone(zone);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+static int restore_highmem(void)
+{
+ printk("swsusp: Restoring Highmem\n");
+ while (highmem_copy) {
+ struct highmem_page *save = highmem_copy;
+ void *kaddr;
+ highmem_copy = save->next;
+
+ kaddr = kmap_atomic(save->page, KM_USER0);
+ memcpy(kaddr, save->data, PAGE_SIZE);
+ kunmap_atomic(kaddr, KM_USER0);
+ free_page((long) save->data);
+ kfree(save);
+ }
+ return 0;
+}
#else
static int save_highmem(void) { return 0; }
static int restore_highmem(void) { return 0; }
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2005-12-28 0:36 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-12-28 0:08 [RFC/RFT][PATCH -mm 0/4] swsusp: userland interface Rafael J. Wysocki
2005-12-28 0:17 ` [RFC/RFT][PATCH -mm 1/4] swsusp: low level interface Rafael J. Wysocki
2005-12-28 0:25 ` [RFC/RFT][PATCH -mm 2/4] swsusp: userland interface Rafael J. Wysocki
2005-12-28 0:28 ` [RFC/RFT][PATCH -mm 3/4] swsusp: separate swap-writing and reading code Rafael J. Wysocki
2005-12-28 0:36 ` [RFC/RFT][PATCH -mm 4/4] swsusp: move highmem-handling code to swsusp.c Rafael J. Wysocki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox