* [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
@ 2006-01-04 22:40 Rafael J. Wysocki
2006-01-04 22:47 ` [RFC/RFT][PATCH -mm 1/5] swsusp: swsusp: low level " Rafael J. Wysocki
` (6 more replies)
0 siblings, 7 replies; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-04 22:40 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 2601 bytes --]
Hi,
This is the second "preview release" of the swsusp userland interface patches.
They have changed quite a bit since the previous post, as I tried to make the
interface more robust against some potential user space bugs (or outright
attempts to abuse it).
The swsusp userland 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). It also modifies the way in
which allocated swap pages are handled (traced) by swsusp and, consequently,
simplifies the in-kernel swap-writing code (a little).
The last three patches are auxiliary. The third one moves the in-kernel
swap-writing and reading code to a separate file, called swap.c, the fourth
one moves the highmem-handling code to swsusp.c, and the last one
provides some documentation and makes it possible to compile out
the in-kernel swap-writing/reading code (or the userland interface, if
desired).
The patches apply on top of three swsusp-related patches that have just
made it into the -mm tree, so hopefully they will apply to the next -mm.
Anyway, 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: 3556 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_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#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_IOCFREE _IO(SNAPSHOT_IOC_MAGIC, 5)
#define SNAPSHOT_IOCSET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
#define SNAPSHOT_IOCAVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
#define SNAPSHOT_IOCGET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
#define SNAPSHOT_IOCFREE_SWAP_PAGES _IO(SNAPSHOT_IOC_MAGIC, 9)
#define SNAPSHOT_IOCSET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
#define SNAPSHOT_IOC_MAXNR 10
#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"
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)));
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 free_snapshot(int dev)
{
return ioctl(dev, SNAPSHOT_IOCFREE, 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 a swap partition. It consists of many swap_map_page
* structures that contain each an array of MAP_PAGE_SIZE swap entries.
* These structures are stored on the swap and linked together with the
* help of the .next_swap member.
*
* The swap map is created during suspend. The swap map pages are
* allocated and populated one at a time, so we only need one memory
* page to set up the entire structure.
*
* During resume we also only need to use one swap_map_page structure
* at a time.
*/
#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1)
struct swap_map_page {
unsigned long entries[MAP_PAGE_ENTRIES];
unsigned long next_swap;
};
#define SNAPSHOT_DEVICE "/dev/snapshot"
#define RESUME_DEVICE "/dev/hdc3"
#define DEFAULT_IMAGE_SIZE 500
[-- Attachment #3: suspend.c --]
[-- Type: text/x-csrc, Size: 7195 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 struct swsusp_header swsusp_header;
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_pages(void)
{
return ioctl(dev, SNAPSHOT_IOCFREE_SWAP_PAGES, 0);
}
static inline int set_swap_file(dev_t blkdev)
{
return ioctl(dev, SNAPSHOT_IOCSET_SWAP_FILE, blkdev);
}
/**
* write_page - Write one page to given swap location.
* @fd: File handle of the resume partition
* @buf: Pointer to the area we're writing.
* @swap_offset: Offset of the swap page we're writing to.
*/
static int write_page(int fd, void *buf, unsigned long swap_offset)
{
off_t offset;
int res = -EINVAL;
ssize_t cnt = 0;
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)
res = 0;
else
res = cnt < 0 ? cnt : -EIO;
}
return res;
}
/*
* The swap_map_handle structure is used for handling swap in
* a file-alike way
*/
struct swap_map_handle {
struct swap_map_page cur;
unsigned long cur_swap;
unsigned int k;
int fd;
};
static inline int init_swap_writer(struct swap_map_handle *handle, int fd)
{
memset(&handle->cur, 0, PAGE_SIZE);
handle->cur_swap = get_swap_page();
if (!handle->cur_swap)
return -ENOSPC;
handle->k = 0;
handle->fd = fd;
return 0;
}
static int swap_write_page(struct swap_map_handle *handle, void *buf)
{
int error;
unsigned long offset;
offset = get_swap_page();
error = write_page(handle->fd, buf, offset);
if (error)
return error;
handle->cur.entries[handle->k++] = offset;
if (handle->k >= MAP_PAGE_ENTRIES) {
offset = get_swap_page();
if (!offset)
return -ENOSPC;
handle->cur.next_swap = offset;
error = write_page(handle->fd, &handle->cur, handle->cur_swap);
if (error)
return error;
memset(&handle->cur, 0, PAGE_SIZE);
handle->cur_swap = offset;
handle->k = 0;
}
return 0;
}
static inline int flush_swap_writer(struct swap_map_handle *handle)
{
if (handle->cur_swap)
return write_page(handle->fd, &handle->cur, handle->cur_swap);
else
return -EINVAL;
}
/**
* 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_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_swap(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)
{
static 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;
}
error = init_swap_writer(&handle, fd);
if (!error) {
start = handle.cur_swap;
error = swap_write_page(&handle, header);
}
if (!error)
error = save_image(&handle, header->pages - 1);
if (!error) {
flush_swap_writer(&handle);
printf( "S" );
error = mark_swap(fd, start);
printf( "|\n" );
}
fsync(fd);
close(fd);
return error;
}
int main(int argc, char *argv[])
{
char *snapshot_device_name, *resume_device_name;
struct stat *stat_buf;
int image_size;
int error;
int in_suspend;
resume_device_name = argc <= 2 ? RESUME_DEVICE : argv[2];
snapshot_device_name = argc <= 1 ? SNAPSHOT_DEVICE : argv[1];
stat_buf = (struct stat *)buffer;
if (stat(resume_device_name, stat_buf)) {
fprintf(stderr, "Could not stat the resume device file\n");
return -ENODEV;
}
if (!S_ISBLK(stat_buf->st_mode)) {
fprintf(stderr, "Invalid resume device\n");
return -EINVAL;
}
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;
}
error = 0;
dev = open(snapshot_device_name, O_RDONLY);
if (dev < 0)
return -ENOENT;
if (set_swap_file(stat_buf->st_rdev)) {
fprintf(stderr, "Could not set the resume device file\n");
error = errno;
goto Close;
}
image_size = check_free_swap();
if (image_size <= 0) {
fprintf(stderr, "No swap space for suspend\n");
error = -ENOSPC;
goto Close;
}
if (image_size > DEFAULT_IMAGE_SIZE)
image_size = DEFAULT_IMAGE_SIZE;
if (freeze(dev)) {
fprintf(stderr, "Could not freeze processes\n");
error = errno;
goto Close;
}
while (image_size >= 0) {
if (set_image_size(dev, image_size)) {
fprintf(stderr, "Could not set the image size\n");
error = errno;
goto Unfreeze;
}
if (!atomic_snapshot(dev, &in_suspend)) {
if (!in_suspend)
break;
if (!write_image(resume_device_name)) {
power_off();
} else {
free_swap_pages();
free_snapshot(dev);
image_size -= DEFAULT_IMAGE_SIZE;
}
}
}
Unfreeze:
unfreeze(dev);
Close:
close(dev);
return error;
}
[-- Attachment #4: resume.c --]
[-- Type: text/x-csrc, Size: 5098 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];
static struct swsusp_header swsusp_header;
/**
* 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 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;
};
/**
* The following functions allow us to read data using a swap map
* in a file-alike way
*/
static inline int init_swap_reader(struct swap_map_handle *handle,
int fd, unsigned long start)
{
int error;
if (!start)
return -EINVAL;
memset(&handle->cur, 0, PAGE_SIZE);
error = read_page(fd, &handle->cur, start);
if (error)
return error;
handle->fd = fd;
handle->k = 0;
return 0;
}
static inline int swap_read_page(struct swap_map_handle *handle, void *buf)
{
unsigned long offset;
int error;
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_ENTRIES) {
handle->k = 0;
offset = handle->cur.next_swap;
if (offset)
error = read_page(handle->fd, &handle->cur, offset);
else
error = -EINVAL;
}
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_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)
{
static struct swap_map_handle handle;
int fd, ret, error = 0;
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 = init_swap_reader(&handle, fd, swsusp_header.image);
if (!error) {
header = (struct swsusp_info *)buffer;
error = swap_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);
}
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;
int error = 0;
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");
error = errno;
goto Close;
}
if (freeze(dev)) {
fprintf(stderr, "resume: Could not freeze processes\n");
error = errno;
goto Close;
}
atomic_restore(dev);
unfreeze(dev);
Close:
close(dev);
return error;
}
[-- Attachment #5: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* [RFC/RFT][PATCH -mm 1/5] swsusp: swsusp: low level interface (rev. 2)
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
@ 2006-01-04 22:47 ` Rafael J. Wysocki
2006-01-04 22:51 ` [RFC/RFT][PATCH -mm 2/5] swsusp: userland " Rafael J. Wysocki
` (5 subsequent siblings)
6 siblings, 0 replies; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-04 22:47 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
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/reading
code of swsusp and the userland interface code introduced by the next patch.
It also changes the way in which swsusp records the allocated swap pages
and, consequently, simplifies the in-kernel swap-writing/reading code
(this is necessary for the userland interface too).
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 | 713 +++++++++++++++++++-----------------------------
4 files changed, 631 insertions(+), 443 deletions(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/disk.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/disk.c 2006-01-04 00:48:40.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/disk.c 2006-01-04 23:14:43.000000000 +0100
@@ -26,9 +26,9 @@
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 @@
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 @@
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 @@
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 2006-01-04 00:48:40.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/power.h 2006-01-04 23:14:49.000000000 +0100
@@ -54,15 +54,26 @@
/* 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_loaded(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 2006-01-04 00:48:40.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2006-01-04 23:14:49.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 @@
char sig[10];
} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;
-static struct swsusp_info swsusp_info;
-
/*
* Saving part...
*/
@@ -129,17 +129,18 @@
return error;
}
-/*
+/**
* Check whether the swap device is the specified resume
* device, irrespective of whether they are specified by
* identical names.
*
* (Thus, device inode aliasing is allowed. You can say /dev/hda4
- * instead of /dev/ide/host0/bus0/target0/lun0/part4 [if using devfs]
- * and they'll be considered the same device. This is *necessary* for
- * devfs, since the resume code can only recognize the form /dev/hda4,
+ * instead of /dev/ide/host0/bus0/target0/lun0/part4 [eg. for devfs]
+ * and they'll be considered the same device. This was *necessary* for
+ * devfs, since the resume code could 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;
@@ -149,7 +150,11 @@
swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
}
-static int swsusp_swap_check(void) /* This is called before saving image */
+/**
+ * swsusp_get_swap_index - find the index of the resume device
+ */
+
+int swsusp_get_swap_index(void)
{
int i;
@@ -161,223 +166,281 @@
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;
}
-/**
- * write_page - Write one page to a fresh swap location.
- * @addr: Address we're writing.
- * @loc: Place to store the entry we used.
- *
- * Allocate a new swap entry and 'sync' it. Note we discard -EIO
- * errors. That is an artifact left over from swsusp. It did not
- * check the return of rw_swap_page_sync() at all, since most pages
- * written back to swap would return -EIO.
- * This is a partial improvement, since we will at least return other
- * errors, though we need to eventually fix the damn code.
- */
-static int write_page(unsigned long addr, swp_entry_t *loc)
+static int swsusp_swap_check(void) /* This is called before saving image */
{
- swp_entry_t entry;
- int error = -ENOSPC;
+ int res = swsusp_get_swap_index();
- 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)
- *loc = entry;
+ if (res >= 0) {
+ root_swap = res;
+ return 0;
}
- return error;
+ return res;
}
/**
- * 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 bitmap is used for tracing allocated swap pages
*
- * 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.
+ * The entire bitmap consists of a number of bitmap_page
+ * structures linked with the help of the .next member.
+ * Thus each page can be allocated individually, so we only
+ * need to make 0-order memory allocations to create
+ * the bitmap.
*/
-#define MAP_PAGE_SIZE ((PAGE_SIZE - sizeof(swp_entry_t) - sizeof(void *)) \
- / sizeof(swp_entry_t))
+#define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *))
+#define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long))
+#define BITS_PER_CHUNK (sizeof(long) * 8)
+#define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK)
-struct swap_map_page {
- swp_entry_t entries[MAP_PAGE_SIZE];
- swp_entry_t next_swap;
- struct swap_map_page *next;
+struct bitmap_page {
+ unsigned long chunks[BITMAP_PAGE_CHUNKS];
+ struct bitmap_page *next;
};
-static inline void free_swap_map(struct swap_map_page *swap_map)
+/**
+ * The following functions are used for tracing the allocated
+ * swap pages, so that they can be freed in case of an error.
+ *
+ * The functions operate on a linked bitmap structure defined
+ * above
+ */
+
+static void free_bitmap(struct bitmap_page *bitmap)
{
- struct swap_map_page *swp;
+ struct bitmap_page *bp;
- while (swap_map) {
- swp = swap_map->next;
- free_page((unsigned long)swap_map);
- swap_map = swp;
+ while (bitmap) {
+ bp = bitmap->next;
+ free_page((unsigned long)bitmap);
+ bitmap = bp;
}
}
-static struct swap_map_page *alloc_swap_map(unsigned int nr_pages)
+static struct bitmap_page *alloc_bitmap(unsigned int nr_bits)
{
- struct swap_map_page *swap_map, *swp;
- unsigned n = 0;
+ struct bitmap_page *bitmap, *bp;
+ unsigned int n;
- if (!nr_pages)
+ if (!nr_bits)
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);
+ bitmap = (struct bitmap_page *)get_zeroed_page(GFP_KERNEL);
+ bp = bitmap;
+ for (n = BITMAP_PAGE_BITS; n < nr_bits; n += BITMAP_PAGE_BITS) {
+ bp->next = (struct bitmap_page *)get_zeroed_page(GFP_KERNEL);
+ bp = bp->next;
+ if (!bp) {
+ free_bitmap(bitmap);
return NULL;
}
}
- return swap_map;
+ return bitmap;
}
-/**
- * reverse_swap_map - reverse the order of pages in the swap map
- * @swap_map
- */
+static inline int bitmap_set(struct bitmap_page *bitmap, unsigned long bit)
+{
+ unsigned int n;
+
+ n = BITMAP_PAGE_BITS;
+ while (bitmap && n <= bit) {
+ n += BITMAP_PAGE_BITS;
+ bitmap = bitmap->next;
+ }
+ if (!bitmap)
+ return -EINVAL;
+ n -= BITMAP_PAGE_BITS;
+ bit -= n;
+ n = 0;
+ while (bit >= BITS_PER_CHUNK) {
+ bit -= BITS_PER_CHUNK;
+ n++;
+ }
+ bitmap->chunks[n] |= (1UL << bit);
+ return 0;
+}
-static inline struct swap_map_page *reverse_swap_map(struct swap_map_page *swap_map)
+static unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap)
{
- struct swap_map_page *prev, *next;
+ unsigned long offset;
- prev = NULL;
- while (swap_map) {
- next = swap_map->next;
- swap_map->next = prev;
- prev = swap_map;
- swap_map = next;
+ offset = swp_offset(get_swap_page_of_type(swap));
+ if (offset) {
+ if (bitmap_set(bitmap, offset)) {
+ swap_free(swp_entry(swap, offset));
+ offset = 0;
+ }
}
- return prev;
+ return offset;
}
-/**
- * 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)
+static void free_all_swap_pages(int swap, struct bitmap_page *bitmap)
{
- while (swap_map) {
- if (swap_map->next_swap.val)
- swap_free(swap_map->next_swap);
- swap_map = swap_map->next;
+ unsigned int bit, n;
+ unsigned long test;
+
+ bit = 0;
+ while (bitmap) {
+ for (n = 0; n < BITMAP_PAGE_CHUNKS; n++)
+ for (test = 1UL; test; test <<= 1) {
+ if (bitmap->chunks[n] & test)
+ swap_free(swp_entry(swap, bit));
+ bit++;
+ }
+ bitmap = bitmap->next;
}
}
/**
- * save_swap_map - save the swap map used for tracing the data pages
- * stored in the swap
+ * write_page - Write one page to given swap location.
+ * @buf: Address we're writing.
+ * @offset: Offset of the swap page we're writing to.
*/
-static int save_swap_map(struct swap_map_page *swap_map, swp_entry_t *start)
+static int write_page(void *buf, unsigned long offset)
{
- swp_entry_t entry = (swp_entry_t){0};
- int error;
+ swp_entry_t entry;
+ int error = -ENOSPC;
- while (swap_map) {
- swap_map->next_swap = entry;
- if ((error = write_page((unsigned long)swap_map, &entry)))
- return error;
- swap_map = swap_map->next;
+ if (offset) {
+ entry = swp_entry(root_swap, offset);
+ error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
}
- *start = entry;
- return 0;
+ return error;
}
/**
- * free_image_entries - free the swap entries allocated to store
- * the image data pages (this is only called in case of an error)
+ * swsusp_total_swap - check the total amount of swap
+ * space in the resume partition/file
*/
-static inline void free_image_entries(struct swap_map_page *swp)
+unsigned int swsusp_total_swap(unsigned int swap)
{
- unsigned k;
+ unsigned int n = 0;
- while (swp) {
- for (k = 0; k < MAP_PAGE_SIZE; k++)
- if (swp->entries[k].val)
- swap_free(swp->entries[k]);
- swp = swp->next;
+ if (swap < MAX_SWAPFILES) {
+ spin_lock(&swap_lock);
+ if (swap_info[swap].flags & SWP_WRITEOK)
+ n = swap_info[swap].pages;
+ spin_unlock(&swap_lock);
}
+ return n;
}
+/*
+ * The swap map is a data structure used for keeping track of each page
+ * written to a swap partition. It consists of many swap_map_page
+ * structures that contain each an array of MAP_PAGE_SIZE swap entries.
+ * These structures are stored on the swap and linked together with the
+ * help of the .next_swap member.
+ *
+ * The swap map is created during suspend. The swap map pages are
+ * allocated and populated one at a time, so we only need one memory
+ * page to set up the entire structure.
+ *
+ * During resume we also only need to use one swap_map_page structure
+ * at a time.
+ */
+
+#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1)
+
+struct swap_map_page {
+ unsigned long entries[MAP_PAGE_ENTRIES];
+ unsigned long next_swap;
+};
+
/**
- * The swap_map_handle structure is used for handling the swap map in
+ * The swap_map_handle structure is used for handling swap in
* a file-alike way
*/
struct swap_map_handle {
struct swap_map_page *cur;
+ unsigned long cur_swap;
+ struct bitmap_page *bitmap;
unsigned int k;
};
-static inline void init_swap_map_handle(struct swap_map_handle *handle,
- struct swap_map_page *map)
+static inline void release_swap_writer(struct swap_map_handle *handle)
{
- handle->cur = map;
+ if (handle->cur)
+ free_page((unsigned long)handle->cur);
+ handle->cur = NULL;
+ if (handle->bitmap)
+ free_bitmap(handle->bitmap);
+ handle->bitmap = NULL;
+}
+
+static inline int get_swap_writer(struct swap_map_handle *handle)
+{
+ handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
+ if (!handle->cur)
+ return -ENOMEM;
+ handle->bitmap = alloc_bitmap(swsusp_total_swap(root_swap));
+ if (!handle->bitmap) {
+ release_swap_writer(handle);
+ return -ENOMEM;
+ }
+ handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap);
+ if (!handle->cur_swap) {
+ release_swap_writer(handle);
+ return -ENOSPC;
+ }
handle->k = 0;
+ return 0;
}
-static inline int swap_map_write_page(struct swap_map_handle *handle,
- unsigned long addr)
+static inline int swap_write_page(struct swap_map_handle *handle, void *buf)
{
int error;
+ unsigned long offset;
- error = write_page(addr, handle->cur->entries + handle->k);
+ if (!handle->cur)
+ return -EINVAL;
+ offset = alloc_swap_page(root_swap, handle->bitmap);
+ error = write_page(buf, offset);
if (error)
return error;
- if (++handle->k >= MAP_PAGE_SIZE) {
- handle->cur = handle->cur->next;
+ handle->cur->entries[handle->k++] = offset;
+ if (handle->k >= MAP_PAGE_ENTRIES) {
+ offset = alloc_swap_page(root_swap, handle->bitmap);
+ if (!offset)
+ return -ENOSPC;
+ handle->cur->next_swap = offset;
+ error = write_page(handle->cur, handle->cur_swap);
+ if (error)
+ return error;
+ memset(handle->cur, 0, PAGE_SIZE);
+ handle->cur_swap = offset;
handle->k = 0;
}
return 0;
}
+static inline int flush_swap_writer(struct swap_map_handle *handle)
+{
+ if (handle->cur && handle->cur_swap)
+ return write_page(handle->cur, handle->cur_swap);
+ else
+ return -EINVAL;
+}
+
/**
- * 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 +448,39 @@
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_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)
- */
-
-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;
-}
-
/**
- * save_image_metadata - save the .orig_address fields of the PBEs
- * from the list @pblist using the swap map handle @handle
+ * swsusp_available_swap - check the total amount of swap
+ * space avaiable from given swap partition/file
*/
-static int save_image_metadata(struct pbe *pblist,
- struct swap_map_handle *handle)
+unsigned int swsusp_available_swap(unsigned int swap)
{
- 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 +492,7 @@
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,57 +508,44 @@
* 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;
- swp_entry_t start;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
+ unsigned long start;
int error;
if ((error = swsusp_swap_check())) {
printk(KERN_ERR "swsusp: Cannot find swap device, try swapon -a.\n");
return error;
}
- if (!enough_swap(nr_pages)) {
+ 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);
+ if (!enough_swap(header->pages)) {
printk(KERN_ERR "swsusp: Not enough free swap\n");
return -ENOSPC;
}
-
- init_header(nr_pages);
- swap_map = alloc_swap_map(swsusp_info.pages);
- if (!swap_map)
- return -ENOMEM;
- init_swap_map_handle(&handle, swap_map);
-
- error = swap_map_write_page(&handle, (unsigned long)&swsusp_info);
- if (!error)
- error = save_image_metadata(pblist, &handle);
+ error = get_swap_writer(&handle);
+ if (!error) {
+ start = handle.cur_swap;
+ error = swap_write_page(&handle, header);
+ }
if (!error)
- error = save_image_data(pblist, &handle, 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;
-
- dump_info();
- printk( "S" );
- error = mark_swapfiles(start);
- printk( "|\n" );
+ error = save_image(&handle, &snapshot, header->pages - 1);
+ if (!error) {
+ flush_swap_writer(&handle);
+ printk("S");
+ error = mark_swapfiles(swp_entry(root_swap, start));
+ printk("|\n");
+ }
if (error)
- goto Free_map_entries;
-
-Free_swap_map:
- free_swap_map(swap_map);
+ free_all_swap_pages(root_swap, handle.bitmap);
+ release_swap_writer(&handle);
return error;
-
-Free_map_entries:
- free_swap_map_entries(swap_map);
-Free_image_entries:
- free_image_entries(swap_map);
- goto Free_swap_map;
}
/**
@@ -663,45 +655,6 @@
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
@@ -781,14 +734,14 @@
* in a file-alike way
*/
-static inline void release_swap_map_reader(struct swap_map_handle *handle)
+static inline void release_swap_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,
+static inline int get_swap_reader(struct swap_map_handle *handle,
swp_entry_t start)
{
int error;
@@ -800,149 +753,80 @@
return -ENOMEM;
error = bio_read_page(swp_offset(start), handle->cur);
if (error) {
- release_swap_map_reader(handle);
+ release_swap_reader(handle);
return error;
}
handle->k = 0;
return 0;
}
-static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf)
+static inline int swap_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]);
+ offset = handle->cur->entries[handle->k];
if (!offset)
- return -EINVAL;
+ return -EFAULT;
error = bio_read_page(offset, buf);
if (error)
return error;
- if (++handle->k >= MAP_PAGE_SIZE) {
+ if (++handle->k >= MAP_PAGE_ENTRIES) {
handle->k = 0;
- offset = swp_offset(handle->cur->next_swap);
+ offset = handle->cur->next_swap;
if (!offset)
- release_swap_map_reader(handle);
+ release_swap_reader(handle);
else
error = bio_read_page(offset, handle->cur);
}
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_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_loaded(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,38 +834,19 @@
return PTR_ERR(resume_bdev);
}
- error = get_swap_map_reader(&handle, swsusp_header.image);
+ 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_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_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);
+ release_swap_reader(&handle);
blkdev_put(resume_bdev);
Index: linux-2.6.15-rc5-mm3/kernel/power/snapshot.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/snapshot.c 2006-01-04 00:47:36.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/snapshot.c 2006-01-04 23:14:44.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 @@
* 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 @@
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 @@
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 @@
}
}
}
+ nr_copy_pages = 0;
+ nr_meta_pages = 0;
+ pagedir_nosave = NULL;
}
@@ -439,7 +443,7 @@
(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 @@
*/
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_loaded(struct snapshot_handle *handle)
+{
+ return !(!handle->pbe || handle->pbe->next || !nr_copy_pages ||
+ handle->page <= nr_meta_pages + nr_copy_pages);
+}
^ permalink raw reply [flat|nested] 19+ messages in thread
* [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
2006-01-04 22:47 ` [RFC/RFT][PATCH -mm 1/5] swsusp: swsusp: low level " Rafael J. Wysocki
@ 2006-01-04 22:51 ` Rafael J. Wysocki
2006-01-04 23:49 ` [linux-pm] " Greg KH
2006-01-04 22:53 ` [RFC/RFT][PATCH -mm 3/5] swsusp: separate swap-writing and reading code " Rafael J. Wysocki
` (4 subsequent siblings)
6 siblings, 1 reply; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-04 22:51 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 17332 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 swap pages so that they know which sectors of the resume partition
are available to them (it is also possible to free the allocated swap pages).
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 in the future, if possible/acceptable.
The interface uses the same low-level snapshot-handling functions that
are used by the in-kernel swap-writing/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 | 46 ++++++
kernel/power/swsusp.c | 69 ++++------
kernel/power/user.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 402 insertions(+), 41 deletions(-)
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 2006-01-04 20:27:47.000000000 +0100
@@ -0,0 +1,325 @@
+/*
+ * 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"
+
+struct snapshot_dev {
+ char *name;
+ dev_t devno;
+ struct cdev cdev;
+ struct snapshot_handle handle;
+ int swap;
+ struct bitmap_page *bitmap;
+ int mode;
+ char frozen;
+ char ready;
+};
+
+static atomic_t device_available = ATOMIC_INIT(1);
+
+int snapshot_open(struct inode *inode, struct file *filp)
+{
+ struct snapshot_dev *dev;
+
+ 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);
+ dev = container_of(inode->i_cdev, struct snapshot_dev, cdev);
+ filp->private_data = dev;
+ memset(&dev->handle, 0, sizeof(struct snapshot_handle));
+ if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
+ dev->swap = swsusp_get_swap_index();
+ dev->mode = O_RDONLY;
+ } else {
+ dev->swap = -1;
+ dev->mode = O_WRONLY;
+ }
+ dev->bitmap = NULL;
+ dev->frozen = 0;
+ dev->ready = 0;
+
+ return 0;
+}
+
+int snapshot_release(struct inode *inode, struct file *filp)
+{
+ struct snapshot_dev *dev;
+
+ swsusp_free();
+ dev = filp->private_data;
+ free_all_swap_pages(dev->swap, dev->bitmap);
+ free_bitmap(dev->bitmap);
+ if (dev->frozen) {
+ down(&pm_sem);
+ thaw_processes();
+ enable_nonboot_cpus();
+ up(&pm_sem);
+ }
+ 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 *dev;
+ ssize_t res;
+
+ dev = filp->private_data;
+ res = snapshot_read_next(&dev->handle, count);
+ if (res > 0) {
+ if (copy_to_user(buf, data_of(dev->handle), res))
+ res = -EFAULT;
+ else
+ *offp = dev->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 *dev;
+ ssize_t res;
+
+ dev = filp->private_data;
+ res = snapshot_write_next(&dev->handle, count);
+ if (res > 0) {
+ if (copy_from_user(data_of(dev->handle), buf, res))
+ res = -EFAULT;
+ else
+ *offp = dev->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 *dev;
+ 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;
+
+ dev = filp->private_data;
+
+ switch (cmd) {
+
+ case SNAPSHOT_IOCFREEZE:
+ if (dev->frozen)
+ break;
+ sys_sync();
+ down(&pm_sem);
+ disable_nonboot_cpus();
+ if (freeze_processes())
+ error = -EBUSY;
+ up(&pm_sem);
+ if (!error)
+ dev->frozen = 1;
+ break;
+
+ case SNAPSHOT_IOCUNFREEZE:
+ if (!dev->frozen)
+ break;
+ down(&pm_sem);
+ thaw_processes();
+ enable_nonboot_cpus();
+ up(&pm_sem);
+ dev->frozen = 0;
+ break;
+
+ case SNAPSHOT_IOCATOMIC_SNAPSHOT:
+ if (dev->mode != O_RDONLY || !dev->frozen || dev->ready) {
+ 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);
+ if (!error)
+ dev->ready = 1;
+ break;
+
+ case SNAPSHOT_IOCATOMIC_RESTORE:
+ if (dev->mode != O_WRONLY || !dev->frozen ||
+ !snapshot_image_loaded(&dev->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_IOCFREE:
+ swsusp_free();
+ memset(&dev->handle, 0, sizeof(struct snapshot_handle));
+ dev->ready = 0;
+ break;
+
+ case SNAPSHOT_IOCSET_IMAGE_SIZE:
+ image_size = arg;
+ break;
+
+ case SNAPSHOT_IOCAVAIL_SWAP:
+ n = swsusp_available_swap(dev->swap);
+ error = put_user(n, (unsigned int __user *)arg);
+ break;
+
+ case SNAPSHOT_IOCGET_SWAP_PAGE:
+ if (!access_ok(VERIFY_WRITE, (unsigned long __user *)arg, _IOC_SIZE(cmd))) {
+ error = -EINVAL;
+ break;
+ }
+ if (dev->swap < 0 || dev->swap >= MAX_SWAPFILES) {
+ error = -ENODEV;
+ break;
+ }
+ if (!dev->bitmap) {
+ dev->bitmap = alloc_bitmap(swsusp_total_swap(dev->swap));
+ if (!dev->bitmap) {
+ error = -ENOMEM;
+ break;
+ }
+ }
+ offset = alloc_swap_page(dev->swap, dev->bitmap);
+ if (offset)
+ __put_user(offset, (unsigned long __user *)arg);
+ else
+ error = -ENOSPC;
+ break;
+
+ case SNAPSHOT_IOCFREE_SWAP_PAGES:
+ if (dev->swap >= 0 && dev->swap < MAX_SWAPFILES) {
+ error = -ENODEV;
+ break;
+ }
+ free_all_swap_pages(dev->swap, dev->bitmap);
+ free_bitmap(dev->bitmap);
+ dev->bitmap = NULL;
+ break;
+
+ case SNAPSHOT_IOCSET_SWAP_FILE:
+ if (!dev->bitmap) {
+ /*
+ * User space encodes device types as two-byte values,
+ * so we need to recode them
+ */
+ dev->swap = swsusp_get_swap_index_of(old_decode_dev(arg));
+ if (dev->swap < 0)
+ error = -ENODEV;
+ } else {
+ error = -EPERM;
+ }
+ 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-31 15:54:11.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/Makefile 2006-01-04 20:26:57.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-31 15:54:11.000000000 +0100
+++ linux-2.6.15-rc5-mm3/init/do_mounts_initrd.c 2005-12-31 17:29:04.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-31 16:05:33.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/power.h 2006-01-04 20:26:57.000000000 +0100
@@ -77,3 +77,49 @@ 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_loaded(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_IOCFREE _IO(SNAPSHOT_IOC_MAGIC, 5)
+#define SNAPSHOT_IOCSET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
+#define SNAPSHOT_IOCAVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
+#define SNAPSHOT_IOCGET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
+#define SNAPSHOT_IOCFREE_SWAP_PAGES _IO(SNAPSHOT_IOC_MAGIC, 9)
+#define SNAPSHOT_IOCSET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
+#define SNAPSHOT_IOC_MAXNR 10
+
+/**
+ * The bitmap is used for tracing allocated swap pages
+ *
+ * The entire bitmap consists of a number of bitmap_page
+ * structures linked with the help of the .next member.
+ * Thus each page can be allocated individually, so we only
+ * need to make 0-order memory allocations to create
+ * the bitmap.
+ */
+
+#define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *))
+#define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long))
+#define BITS_PER_CHUNK (sizeof(long) * 8)
+#define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK)
+
+struct bitmap_page {
+ unsigned long chunks[BITMAP_PAGE_CHUNKS];
+ struct bitmap_page *next;
+};
+
+extern void free_bitmap(struct bitmap_page *bitmap);
+extern struct bitmap_page *alloc_bitmap(unsigned int nr_bits);
+extern unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap);
+extern void free_all_swap_pages(int swap, struct bitmap_page *bitmap);
+
+extern int swsusp_get_swap_index_of(dev_t device);
+extern int swsusp_get_swap_index(void);
+extern unsigned int swsusp_total_swap(unsigned int swap);
+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);
Index: linux-2.6.15-rc5-mm3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/swsusp.c 2005-12-31 16:51:57.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2006-01-04 20:26:57.000000000 +0100
@@ -130,41 +130,41 @@ static int mark_swapfiles(swp_entry_t st
}
/**
- * Check whether the swap device is the specified resume
- * device, irrespective of whether they are specified by
- * identical names.
+ * is_device - check whether the specified device is a swap device,
+ * irrespective of whether they are specified by identical names.
*
- * (Thus, device inode aliasing is allowed. You can say /dev/hda4
- * instead of /dev/ide/host0/bus0/target0/lun0/part4 [eg. for devfs]
- * and they'll be considered the same device. This was *necessary* for
- * devfs, since the resume code could only recognize the form /dev/hda4,
- * but the suspend code would see the long name.)
+ * (Thus, device inode aliasing is allowed. You can say /dev/hda4
+ * instead of /dev/ide/host0/bus0/target0/lun0/part4 [eg. for devfs]
+ * and they'll be considered the same device. This was *necessary* for
+ * devfs, since the resume code could 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)
+static inline int is_device(const struct swap_info_struct *swap_info,
+ dev_t device)
{
struct file *file = swap_info->swap_file;
struct inode *inode = file->f_dentry->d_inode;
return S_ISBLK(inode->i_mode) &&
- swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
+ device == MKDEV(imajor(inode), iminor(inode));
}
/**
- * swsusp_get_swap_index - find the index of the resume device
+ * swsusp_get_swap_index_of - find the index of the given device
*/
-int swsusp_get_swap_index(void)
+int swsusp_get_swap_index_of(dev_t device)
{
int i;
- if (!swsusp_resume_device)
- return -ENODEV;
+ if (!device)
+ return -EINVAL;
spin_lock(&swap_lock);
for (i = 0; i < MAX_SWAPFILES; i++) {
if (!(swap_info[i].flags & SWP_WRITEOK))
continue;
- if (is_resume_device(swap_info + i)) {
+ if (is_device(swap_info + i, device)) {
spin_unlock(&swap_lock);
return i;
}
@@ -173,6 +173,15 @@ int swsusp_get_swap_index(void)
return -ENODEV;
}
+/**
+ * swsusp_get_swap_index - find the index of the resume device
+ */
+
+int swsusp_get_swap_index(void)
+{
+ return swsusp_get_swap_index_of(swsusp_resume_device);
+}
+
static int swsusp_swap_check(void) /* This is called before saving image */
{
int res = swsusp_get_swap_index();
@@ -185,34 +194,14 @@ static int swsusp_swap_check(void) /* Th
}
/**
- * The bitmap is used for tracing allocated swap pages
- *
- * The entire bitmap consists of a number of bitmap_page
- * structures linked with the help of the .next member.
- * Thus each page can be allocated individually, so we only
- * need to make 0-order memory allocations to create
- * the bitmap.
- */
-
-#define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *))
-#define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long))
-#define BITS_PER_CHUNK (sizeof(long) * 8)
-#define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK)
-
-struct bitmap_page {
- unsigned long chunks[BITMAP_PAGE_CHUNKS];
- struct bitmap_page *next;
-};
-
-/**
* The following functions are used for tracing the allocated
* swap pages, so that they can be freed in case of an error.
*
* The functions operate on a linked bitmap structure defined
- * above
+ * in power.h
*/
-static void free_bitmap(struct bitmap_page *bitmap)
+void free_bitmap(struct bitmap_page *bitmap)
{
struct bitmap_page *bp;
@@ -223,7 +212,7 @@ static void free_bitmap(struct bitmap_pa
}
}
-static struct bitmap_page *alloc_bitmap(unsigned int nr_bits)
+struct bitmap_page *alloc_bitmap(unsigned int nr_bits)
{
struct bitmap_page *bitmap, *bp;
unsigned int n;
@@ -266,7 +255,7 @@ static inline int bitmap_set(struct bitm
return 0;
}
-static unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap)
+unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap)
{
unsigned long offset;
@@ -280,7 +269,7 @@ static unsigned long alloc_swap_page(int
return offset;
}
-static void free_all_swap_pages(int swap, struct bitmap_page *bitmap)
+void free_all_swap_pages(int swap, struct bitmap_page *bitmap)
{
unsigned int bit, n;
unsigned long test;
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* [RFC/RFT][PATCH -mm 3/5] swsusp: separate swap-writing and reading code (rev. 2)
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
2006-01-04 22:47 ` [RFC/RFT][PATCH -mm 1/5] swsusp: swsusp: low level " Rafael J. Wysocki
2006-01-04 22:51 ` [RFC/RFT][PATCH -mm 2/5] swsusp: userland " Rafael J. Wysocki
@ 2006-01-04 22:53 ` Rafael J. Wysocki
2006-01-04 22:55 ` [RFC/RFT][PATCH -mm 4/5] swsusp: move highmem-handling code to swsusp.c " Rafael J. Wysocki
` (3 subsequent siblings)
6 siblings, 0 replies; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-04 22:53 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 31144 bytes --]
This patch moves the in-kernel swap-writing/reading code to a separate file.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
kernel/power/Makefile | 2
kernel/power/power.h | 2
kernel/power/swap.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/power/swsusp.c | 535 ------------------------------------------------
4 files changed, 559 insertions(+), 536 deletions(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/Makefile
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/Makefile 2005-12-31 17:56:20.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/Makefile 2005-12-31 17:57:22.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-31 18:12:41.000000000 +0100
@@ -0,0 +1,556 @@
+/*
+ * 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 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;
+
+/**
+ * mark_swapfiles - change the signature of the resume partition
+ * and store the swap address of given entry in the partition's
+ * header (the entry should point to the first image page in the
+ * swap)
+ */
+
+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;
+}
+
+/**
+ * swsusp_swap_check - check if the resume partition is available
+ */
+
+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 given swap location.
+ * @buf: Address we're writing.
+ * @offset: Offset of the swap page we're writing to.
+ */
+
+static int write_page(void *buf, unsigned long offset)
+{
+ swp_entry_t entry;
+ int error = -ENOSPC;
+
+ if (offset) {
+ entry = swp_entry(root_swap, offset);
+ error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
+ }
+ return error;
+}
+
+/*
+ * The swap map is a data structure used for keeping track of each page
+ * written to a swap partition. It consists of many swap_map_page
+ * structures that contain each an array of MAP_PAGE_SIZE swap entries.
+ * These structures are stored on the swap and linked together with the
+ * help of the .next_swap member.
+ *
+ * The swap map is created during suspend. The swap map pages are
+ * allocated and populated one at a time, so we only need one memory
+ * page to set up the entire structure.
+ *
+ * During resume we also only need to use one swap_map_page structure
+ * at a time.
+ */
+
+#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1)
+
+struct swap_map_page {
+ unsigned long entries[MAP_PAGE_ENTRIES];
+ unsigned long next_swap;
+};
+
+/**
+ * The swap_map_handle structure is used for handling swap in
+ * a file-alike way
+ */
+
+struct swap_map_handle {
+ struct swap_map_page *cur;
+ unsigned long cur_swap;
+ struct bitmap_page *bitmap;
+ unsigned int k;
+};
+
+static inline void release_swap_writer(struct swap_map_handle *handle)
+{
+ if (handle->cur)
+ free_page((unsigned long)handle->cur);
+ handle->cur = NULL;
+ if (handle->bitmap)
+ free_bitmap(handle->bitmap);
+ handle->bitmap = NULL;
+}
+
+static inline int get_swap_writer(struct swap_map_handle *handle)
+{
+ handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
+ if (!handle->cur)
+ return -ENOMEM;
+ handle->bitmap = alloc_bitmap(swsusp_total_swap(root_swap));
+ if (!handle->bitmap) {
+ release_swap_writer(handle);
+ return -ENOMEM;
+ }
+ handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap);
+ if (!handle->cur_swap) {
+ release_swap_writer(handle);
+ return -ENOSPC;
+ }
+ handle->k = 0;
+ return 0;
+}
+
+static inline int swap_write_page(struct swap_map_handle *handle, void *buf)
+{
+ int error;
+ unsigned long offset;
+
+ if (!handle->cur)
+ return -EINVAL;
+ offset = alloc_swap_page(root_swap, handle->bitmap);
+ error = write_page(buf, offset);
+ if (error)
+ return error;
+ handle->cur->entries[handle->k++] = offset;
+ if (handle->k >= MAP_PAGE_ENTRIES) {
+ offset = alloc_swap_page(root_swap, handle->bitmap);
+ if (!offset)
+ return -ENOSPC;
+ handle->cur->next_swap = offset;
+ error = write_page(handle->cur, handle->cur_swap);
+ if (error)
+ return error;
+ memset(handle->cur, 0, PAGE_SIZE);
+ handle->cur_swap = offset;
+ handle->k = 0;
+ }
+ return 0;
+}
+
+static inline int flush_swap_writer(struct swap_map_handle *handle)
+{
+ if (handle->cur && handle->cur_swap)
+ return write_page(handle->cur, handle->cur_swap);
+ else
+ return -EINVAL;
+}
+
+/**
+ * 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_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_handle handle;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
+ unsigned long 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);
+ if (!enough_swap(header->pages)) {
+ printk(KERN_ERR "swsusp: Not enough free swap\n");
+ return -ENOSPC;
+ }
+ error = get_swap_writer(&handle);
+ if (!error) {
+ start = handle.cur_swap;
+ error = swap_write_page(&handle, header);
+ }
+ if (!error)
+ error = save_image(&handle, &snapshot, header->pages - 1);
+ if (!error) {
+ flush_swap_writer(&handle);
+ printk("S");
+ error = mark_swapfiles(swp_entry(root_swap, start));
+ printk("|\n");
+ }
+ if (error)
+ free_all_swap_pages(root_swap, handle.bitmap);
+ release_swap_writer(&handle);
+ 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_reader(struct swap_map_handle *handle)
+{
+ if (handle->cur)
+ free_page((unsigned long)handle->cur);
+ handle->cur = NULL;
+}
+
+static inline int get_swap_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_reader(handle);
+ return error;
+ }
+ handle->k = 0;
+ return 0;
+}
+
+static inline int swap_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 = bio_read_page(offset, buf);
+ if (error)
+ return error;
+ if (++handle->k >= MAP_PAGE_ENTRIES) {
+ handle->k = 0;
+ offset = handle->cur->next_swap;
+ if (!offset)
+ release_swap_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_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_loaded(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_reader(&handle, swsusp_header.image);
+ if (!error)
+ error = swap_read_page(&handle, header);
+ if (!error) {
+ nr_pages = header->image_pages;
+ error = load_image(&handle, &snapshot, nr_pages);
+ }
+ release_swap_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/power.h
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/power.h 2005-12-31 17:56:20.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/power.h 2005-12-31 18:13:19.000000000 +0100
@@ -53,8 +53,8 @@ extern struct pbe *pagedir_nosave;
/* Preferred image size in MB (default 500) */
extern unsigned int image_size;
-
extern int in_suspend;
+extern dev_t swsusp_resume_device;
extern asmlinkage int swsusp_arch_suspend(void);
extern asmlinkage int swsusp_arch_resume(void);
Index: linux-2.6.15-rc5-mm3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/swsusp.c 2005-12-31 17:56:20.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2005-12-31 18:13:49.000000000 +0100
@@ -31,41 +31,24 @@
* Fixed runaway init
*
* Rafael J. Wysocki <rjw@sisk.pl>
- * Added the swap map data structure and reworked the handling of swap
+ * Reworked the handling of swap and added the user interface
*
* More state savers are welcome. Especially for the scsi layer...
*
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
*/
-#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
-#include <linux/smp_lock.h>
-#include <linux/file.h>
-#include <linux/utsname.h>
-#include <linux/version.h>
-#include <linux/delay.h>
-#include <linux/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,46 +72,6 @@ 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;
-}
-
/**
* is_device - check whether the specified device is a swap device,
* irrespective of whether they are specified by identical names.
@@ -182,17 +125,6 @@ int swsusp_get_swap_index(void)
return swsusp_get_swap_index_of(swsusp_resume_device);
}
-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;
-}
-
/**
* The following functions are used for tracing the allocated
* swap pages, so that they can be freed in case of an error.
@@ -287,24 +219,6 @@ void free_all_swap_pages(int swap, struc
}
/**
- * write_page - Write one page to given swap location.
- * @buf: Address we're writing.
- * @offset: Offset of the swap page we're writing to.
- */
-
-static int write_page(void *buf, unsigned long offset)
-{
- swp_entry_t entry;
- int error = -ENOSPC;
-
- if (offset) {
- entry = swp_entry(root_swap, offset);
- error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
- }
- return error;
-}
-
-/**
* swsusp_total_swap - check the total amount of swap
* space in the resume partition/file
*/
@@ -322,137 +236,6 @@ unsigned int swsusp_total_swap(unsigned
return n;
}
-/*
- * The swap map is a data structure used for keeping track of each page
- * written to a swap partition. It consists of many swap_map_page
- * structures that contain each an array of MAP_PAGE_SIZE swap entries.
- * These structures are stored on the swap and linked together with the
- * help of the .next_swap member.
- *
- * The swap map is created during suspend. The swap map pages are
- * allocated and populated one at a time, so we only need one memory
- * page to set up the entire structure.
- *
- * During resume we also only need to use one swap_map_page structure
- * at a time.
- */
-
-#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1)
-
-struct swap_map_page {
- unsigned long entries[MAP_PAGE_ENTRIES];
- unsigned long next_swap;
-};
-
-/**
- * The swap_map_handle structure is used for handling swap in
- * a file-alike way
- */
-
-struct swap_map_handle {
- struct swap_map_page *cur;
- unsigned long cur_swap;
- struct bitmap_page *bitmap;
- unsigned int k;
-};
-
-static inline void release_swap_writer(struct swap_map_handle *handle)
-{
- if (handle->cur)
- free_page((unsigned long)handle->cur);
- handle->cur = NULL;
- if (handle->bitmap)
- free_bitmap(handle->bitmap);
- handle->bitmap = NULL;
-}
-
-static inline int get_swap_writer(struct swap_map_handle *handle)
-{
- handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
- if (!handle->cur)
- return -ENOMEM;
- handle->bitmap = alloc_bitmap(swsusp_total_swap(root_swap));
- if (!handle->bitmap) {
- release_swap_writer(handle);
- return -ENOMEM;
- }
- handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap);
- if (!handle->cur_swap) {
- release_swap_writer(handle);
- return -ENOSPC;
- }
- handle->k = 0;
- return 0;
-}
-
-static inline int swap_write_page(struct swap_map_handle *handle, void *buf)
-{
- int error;
- unsigned long offset;
-
- if (!handle->cur)
- return -EINVAL;
- offset = alloc_swap_page(root_swap, handle->bitmap);
- error = write_page(buf, offset);
- if (error)
- return error;
- handle->cur->entries[handle->k++] = offset;
- if (handle->k >= MAP_PAGE_ENTRIES) {
- offset = alloc_swap_page(root_swap, handle->bitmap);
- if (!offset)
- return -ENOSPC;
- handle->cur->next_swap = offset;
- error = write_page(handle->cur, handle->cur_swap);
- if (error)
- return error;
- memset(handle->cur, 0, PAGE_SIZE);
- handle->cur_swap = offset;
- handle->k = 0;
- }
- return 0;
-}
-
-static inline int flush_swap_writer(struct swap_map_handle *handle)
-{
- if (handle->cur && handle->cur_swap)
- return write_page(handle->cur, handle->cur_swap);
- else
- return -EINVAL;
-}
-
-/**
- * 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_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
@@ -473,71 +256,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_handle handle;
- struct snapshot_handle snapshot;
- struct swsusp_info *header;
- unsigned long 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);
- if (!enough_swap(header->pages)) {
- printk(KERN_ERR "swsusp: Not enough free swap\n");
- return -ENOSPC;
- }
- error = get_swap_writer(&handle);
- if (!error) {
- start = handle.cur_swap;
- error = swap_write_page(&handle, header);
- }
- if (!error)
- error = save_image(&handle, &snapshot, header->pages - 1);
- if (!error) {
- flush_swap_writer(&handle);
- printk("S");
- error = mark_swapfiles(swp_entry(root_swap, start));
- printk("|\n");
- }
- if (error)
- free_all_swap_pages(root_swap, handle.bitmap);
- release_swap_writer(&handle);
- return error;
-}
-
-/**
* swsusp_shrink_memory - Try to free as much memory as needed
*
* ... but do not OOM-kill anyone
@@ -643,254 +361,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_reader(struct swap_map_handle *handle)
-{
- if (handle->cur)
- free_page((unsigned long)handle->cur);
- handle->cur = NULL;
-}
-
-static inline int get_swap_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_reader(handle);
- return error;
- }
- handle->k = 0;
- return 0;
-}
-
-static inline int swap_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 = bio_read_page(offset, buf);
- if (error)
- return error;
- if (++handle->k >= MAP_PAGE_ENTRIES) {
- handle->k = 0;
- offset = handle->cur->next_swap;
- if (!offset)
- release_swap_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_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_loaded(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_reader(&handle, swsusp_header.image);
- if (!error)
- error = swap_read_page(&handle, header);
- if (!error) {
- nr_pages = header->image_pages;
- error = load_image(&handle, &snapshot, nr_pages);
- }
- release_swap_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] 19+ messages in thread
* [RFC/RFT][PATCH -mm 4/5] swsusp: move highmem-handling code to swsusp.c (rev. 2)
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
` (2 preceding siblings ...)
2006-01-04 22:53 ` [RFC/RFT][PATCH -mm 3/5] swsusp: separate swap-writing and reading code " Rafael J. Wysocki
@ 2006-01-04 22:55 ` Rafael J. Wysocki
2006-01-04 22:56 ` [RFC/RFT][PATCH -mm 5/5] swsusp: userland interface documentation and config Rafael J. Wysocki
` (2 subsequent siblings)
6 siblings, 0 replies; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-04 22:55 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 6799 bytes --]
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-31 15:48:44.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/snapshot.c 2005-12-31 18:17:17.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-31 18:13:49.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2005-12-31 18:17:17.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; }
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* [RFC/RFT][PATCH -mm 5/5] swsusp: userland interface documentation and config
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
` (3 preceding siblings ...)
2006-01-04 22:55 ` [RFC/RFT][PATCH -mm 4/5] swsusp: move highmem-handling code to swsusp.c " Rafael J. Wysocki
@ 2006-01-04 22:56 ` Rafael J. Wysocki
2006-01-05 1:05 ` [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Pavel Machek
2006-01-05 23:30 ` Pavel Machek
6 siblings, 0 replies; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-04 22:56 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 13228 bytes --]
Documentation/power/userland-swsusp.txt | 120 ++++++++++++++++++++++++++++++++
kernel/power/Kconfig | 74 +++++++++++++------
kernel/power/Makefile | 6 +
kernel/power/disk.c | 1
kernel/power/power.h | 2
kernel/power/swsusp.c | 2
6 files changed, 179 insertions(+), 26 deletions(-)
Index: linux-2.6.15-rc5-mm3/kernel/power/Kconfig
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/Kconfig 2006-01-04 20:26:57.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/Kconfig 2006-01-04 20:28:29.000000000 +0100
@@ -42,27 +42,65 @@ config SOFTWARE_SUSPEND
---help---
Enable the possibility of suspending the machine.
It doesn't need APM.
- You may suspend your machine by 'swsusp' or 'shutdown -z <time>'
- (patch for sysvinit needed).
- It creates an image which is saved in your active swap. Upon next
- boot, pass the 'resume=/dev/swappartition' argument to the kernel to
- have it detect the saved image, restore memory state from it, and
- continue to run as before. If you do not want the previous state to
- be reloaded, then use the 'noresume' kernel argument. However, note
- that your partitions will be fsck'd and you must re-mkswap your swap
- partitions. It does not work with swap files.
+if SOFTWARE_SUSPEND
- Right now you may boot without resuming and then later resume but
+config USERLAND_SUSPEND
+ bool "User interface for software suspend"
+ depends on SOFTWARE_SUSPEND
+ default y
+ ---help---
+ Enable the interface allowing user space processes to handle the suspend
+ and resume of the system.
+
+ If this interface is used for suspend, the kernel creates a snapshot of
+ the system memory that may be encrypted, compressed, etc. and has to be
+ written to a storage by a user space utility. On resume the image
+ is read, uncompressed, decrypted, etc. by a user space utility and
+ transferred to the kernel which restores the system memory state from
+ it.
+
+ This is the recommended way of handling software suspend and the
+ future development will be focused on it, so if you enable software
+ suspend, you should say Y here.
+
+ Of course special user space utilities are necessary to use it.
+
+ For more information please read <file:Documentation/power/userland-swsusp.txt>.
+
+config SWAP_SUSPEND
+ bool "Legacy swap suspend"
+ depends on SOFTWARE_SUSPEND
+ default y
+ ---help---
+ Enable the built-in legacy swap suspend.
+
+ It allows you to suspend the machine by 'echo disk > /sys/power/state'
+ or 'shutdown -z <time>' (patch for sysvinit needed).
+
+ It creates an image which is saved in the swap partition specified
+ with the 'resume=/dev/swappartition' kernel command line argument.
+
+ On the next boot, the 'resume=/dev/swappartition' command line argument
+ causes the kernel to look for the saved image, restore memory state from it,
+ and continue to run as before. If you do not want the previous state to
+ be reloaded, then use the 'noresume' kernel argument. However, note
+ that your partitions will be fsck'd and you must re-mkswap your resume
+ partition. It does not work with swap files.
+
+ In principle you may boot without resuming and then later resume but
in meantime you cannot use those swap partitions/files which were
- involved in suspending. Also in this case there is a risk that buffers
+ involved in suspending. Also in this case there is a risk that buffers
on disk won't match with saved ones.
+ You may want to say Y here if your distribution contains power management
+ utilities that assume this feature to be present.
+
For more information take a look at <file:Documentation/power/swsusp.txt>.
config PM_STD_PARTITION
string "Default resume partition"
- depends on SOFTWARE_SUSPEND
+ depends on SWAP_SUSPEND
default ""
---help---
The default resume partition is the partition that the suspend-
@@ -82,17 +120,7 @@ config PM_STD_PARTITION
suspended image to. It will simply pick the first available swap
device.
-config SWSUSP_ENCRYPT
- bool "Encrypt suspend image"
- depends on SOFTWARE_SUSPEND && CRYPTO=y && (CRYPTO_AES=y || CRYPTO_AES_586=y || CRYPTO_AES_X86_64=y)
- default ""
- ---help---
- To prevent data gathering from swap after resume you can encrypt
- the suspend image with a temporary key that is deleted on
- resume.
-
- Note that the temporary key is stored unencrypted on disk while the
- system is suspended.
+endif # SOFTWARE_SUSPEND
config SUSPEND_SMP
bool
Index: linux-2.6.15-rc5-mm3/kernel/power/Makefile
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/Makefile 2006-01-04 20:28:29.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/Makefile 2006-01-04 20:28:29.000000000 +0100
@@ -5,7 +5,11 @@ 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 swap.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o snapshot.o
+
+obj-$(CONFIG_USERLAND_SUSPEND) += user.o
+
+obj-$(CONFIG_SWAP_SUSPEND) += disk.o swap.o
obj-$(CONFIG_SUSPEND_SMP) += smp.o
Index: linux-2.6.15-rc5-mm3/kernel/power/disk.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/disk.c 2006-01-04 20:26:57.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/disk.c 2006-01-04 20:28:29.000000000 +0100
@@ -35,7 +35,6 @@ extern int swsusp_resume(void);
static int noresume = 0;
char resume_file[256] = CONFIG_PM_STD_PARTITION;
-dev_t swsusp_resume_device;
/**
* power_down - Shut machine down for hibernate.
Index: linux-2.6.15-rc5-mm3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/swsusp.c 2006-01-04 20:28:29.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/swsusp.c 2006-01-04 20:28:29.000000000 +0100
@@ -62,6 +62,8 @@ unsigned int image_size = 500;
int in_suspend __nosavedata = 0;
+dev_t swsusp_resume_device;
+
#ifdef CONFIG_HIGHMEM
static unsigned int count_highmem_pages(void)
{
Index: linux-2.6.15-rc5-mm3/kernel/power/power.h
===================================================================
--- linux-2.6.15-rc5-mm3.orig/kernel/power/power.h 2006-01-04 20:28:29.000000000 +0100
+++ linux-2.6.15-rc5-mm3/kernel/power/power.h 2006-01-04 20:28:29.000000000 +0100
@@ -20,7 +20,7 @@ struct swsusp_info {
-#ifdef CONFIG_SOFTWARE_SUSPEND
+#ifdef CONFIG_SWAP_SUSPEND
extern int pm_suspend_disk(void);
#else
Index: linux-2.6.15-rc5-mm3/Documentation/power/userland-swsusp.txt
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15-rc5-mm3/Documentation/power/userland-swsusp.txt 2006-01-04 20:34:11.000000000 +0100
@@ -0,0 +1,120 @@
+Documentation for userland software suspend interface
+ (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
+
+First, the warnings at the beginning of swsusp.txt still apply.
+
+Second, you should read the FAQ in swsusp.txt _now_ if you have not
+done it already.
+
+Now, to use the userland interface for software suspend you need special
+utilities that will read/write the system memory snapshot from/to the
+kernel. Such utilities are available, for example, from
+<http://www.sisk.pl/kernel/utilities/suspend>. You may want to have
+a look at them if you are going to develop your own suspend/resume
+utilities.
+
+The interface consists of a character device providing the open(),
+release(), read(), and write() operations as well as several ioctl()
+commands defined in kernel/power/power.h.
+
+Currently the device's major number is allocated dynamically, so the
+major:minor pair is exported via sysfs, as the /sys/power/snapshot attribute.
+
+The device can be open either for reading or for writing. If open for
+reading, it is considered to be in the suspend mode. Otherwise it is
+assumed to be in the resume mode. The device cannot be open
+for reading and writing. It is also imposiible to have the device open
+more than once at a time.
+
+The ioctl() commands recognized by the device are:
+
+SNAPSHOT_IOCFREEZE - freeze user space processes (the current process is
+ not frozen); this is required for SNAPSHOT_IOCATOMIC_SNAPSHOT
+ and SNAPSHOT_IOCATOMIC_RESTORE to succeed
+
+SNAPSHOT_IOCUNFREEZE - thaw user space processes frozen by SNAPSHOT_IOCFREEZE
+
+SNAPSHOT_IOCATOMIC_SNAPSHOT - create a snapshot of the system memory; the
+ last argument of ioctl() should be a pointer to an int variable,
+ the value of which will indicate whether the call returned after
+ creating the snapshot (1) or after restoring the system memory state
+ from it (0) (after resume the system finds itself finishing the
+ SNAPSHOT_IOCATOMIC_SNAPSHOT ioctl() again); after the snapshot
+ has been created the read() operation can be used to transfer
+ it out of the kernel
+
+SNAPSHOT_IOCATOMIC_RESTORE - restore the system memory state from the
+ uploaded snapshot image; before calling it you should transfer
+ the systme memory snapshot back to the kernel using the write()
+ operation; this call will not succeed if the snapshot
+ image is not available to the kernel
+
+SNAPSHOT_IOCFREE - free memory allocated for the snapshot image
+
+SNAPSHOT_IOCSET_IMAGE_SIZE - set the preferred maximum size of the image
+ (the kernel will do its best to ensure the image size will not exceed
+ this number, but if it turns out to be impossible, the kernel will
+ create the smallest image possible)
+
+SNAPSHOT_IOCAVAIL_SWAP - check the amount of available swap (the last argument
+ should be a pointer to an unsigned int variable that will contain
+ the result if the call is successful)
+
+SNAPSHOT_IOCGET_SWAP_PAGE - allocate a swap page from the resume partition
+ (the last argument should be a pointer to an unsigned int variable that
+ will contain the swap page offset if the call is successful)
+
+SNAPSHOT_IOCFREE_SWAP_PAGES - free all swap pages allocated with
+ SNAPSHOT_IOCGET_SWAP_PAGE
+
+SNAPSHOT_IOCSET_SWAP_FILE - set the resume partition (the last ioctl() argument
+ should specify the device's major and minor numbers in the old
+ two-byte format, as returned by the stat() function in the .st_rdev
+ member of the stat structure); it is recommended to always use this
+ call, because the other code the could have set the resume partition
+ need not be present in the kernel
+
+The device's read() operation can be used to transfer the snapshot image from
+the kernel. It has the following limitations:
+- you cannot read() more than one virtual memory page at a time
+- read()s accross page boundaries are impossible (ie. if ypu read() 1/2 of
+ a page in the previous call, you will only be able to read()
+ _at_ _most_ 1/2 of the page in the next call)
+
+The device's write() operation is used for uploading the system memory snapshot
+into the kernel. It has the same limitations as the read() operation.
+
+The release() operation frees all memory allocated for the snapshot image
+and all swap pages allocated with SNAPSHOT_IOCGET_SWAP_PAGE (if any).
+Thus it is not necessary to use either SNAPSHOT_IOCFREE or
+SNAPSHOT_IOCFREE_SWAP_PAGES before closing the device (in fact it will also
+unfreeze user space processes frozen by SNAPSHOT_IOCUNFREEZE if they are
+still frozen when the device is being closed).
+
+Currently it is assumed that the userland utilities reading/writing the
+snapshot image from/to the kernel will use a swap parition, called the resume
+partition, as storage space. However, this is not really required, as they
+can use, for example, a special (blank) suspend partition or a file on a partition
+that is unmounted before SNAPSHOT_IOCATOMIC_SNAPSHOT and mounted afterwards.
+
+These utilities SHOULD NOT make any assumptions regarding the ordering of
+data within the snapshot image, except for the image header that MAY be
+assumed to start with an swsusp_info structure, as specified in
+kernel/power/power.h. This structure MAY be used by the userland utilities
+to obtain some information about the snapshot image, such as the total
+number of the image pages, including the metadata and the header itself,
+contained in the .pages member of swsusp_info.
+
+The snapshot image MUST be written to the kernel unaltered (ie. all of the image
+data, metadata and header MUST be written in _exactly_ the same amount, form
+and order in which they have been read). Otherwise, the behavior of the
+resumed system may be totally unpredictable.
+
+While executing SNAPSHOT_IOCATOMIC_RESTORE the kernel checks if the
+structure of the snapshot image is consistent with the information stored
+in the image header. If any inconsistencies are detected,
+SNAPSHOT_IOCATOMIC_RESTORE will not succeed. Still, this is not a fool-proof
+mechanism and the userland utilities using the interface SHOULD use additional
+means, such as checksums, to ensure the integrity of the snapshot image.
+
+For details, please refer to the source code.
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [linux-pm] [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-04 22:51 ` [RFC/RFT][PATCH -mm 2/5] swsusp: userland " Rafael J. Wysocki
@ 2006-01-04 23:49 ` Greg KH
2006-01-05 0:18 ` Pavel Machek
0 siblings, 1 reply; 19+ messages in thread
From: Greg KH @ 2006-01-04 23:49 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Pavel Machek, Linux PM, LKML
On Wed, Jan 04, 2006 at 11:51:58PM +0100, Rafael J. Wysocki wrote:
> +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);
Heh, that's a neat hack, register a sysfs file that contains the
major:minor (there is a function that will print that the correct way,
if you really want to do that), in sysfs. It's better to just register
a misc character device with the name "snapshot", and then udev will
create your userspace node with the proper major:minor all automatically
for you.
Unless you want to turn these into syscalls :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-04 23:49 ` [linux-pm] " Greg KH
@ 2006-01-05 0:18 ` Pavel Machek
2006-01-05 0:26 ` Greg KH
0 siblings, 1 reply; 19+ messages in thread
From: Pavel Machek @ 2006-01-05 0:18 UTC (permalink / raw)
To: Greg KH; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 1098 bytes --]
Hi!
> > +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);
>
> Heh, that's a neat hack, register a sysfs file that contains the
> major:minor (there is a function that will print that the correct way,
> if you really want to do that), in sysfs. It's better to just register
> a misc character device with the name "snapshot", and then udev will
> create your userspace node with the proper major:minor all automatically
> for you.
>
> Unless you want to turn these into syscalls :)
Well, I think we simply want to get static major/minor allocated for
this device. It really uses read/write, IIRC, so no, I do not think we
want to make it a syscall.
Pavel
--
Thanks, Sharp!
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-05 0:18 ` Pavel Machek
@ 2006-01-05 0:26 ` Greg KH
2006-01-05 0:54 ` Pavel Machek
` (2 more replies)
0 siblings, 3 replies; 19+ messages in thread
From: Greg KH @ 2006-01-05 0:26 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 1333 bytes --]
On Thu, Jan 05, 2006 at 01:18:37AM +0100, Pavel Machek wrote:
> Hi!
>
> > > +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);
> >
> > Heh, that's a neat hack, register a sysfs file that contains the
> > major:minor (there is a function that will print that the correct way,
> > if you really want to do that), in sysfs. It's better to just register
> > a misc character device with the name "snapshot", and then udev will
> > create your userspace node with the proper major:minor all automatically
> > for you.
> >
> > Unless you want to turn these into syscalls :)
>
> Well, I think we simply want to get static major/minor allocated for
> this device. It really uses read/write, IIRC, so no, I do not think we
> want to make it a syscall.
Ok, then I'd recommend using the misc device, dynamic for now, and
reserve one when you get a bit closer to merging into mainline.
thanks,
greg k-h
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-05 0:26 ` Greg KH
@ 2006-01-05 0:54 ` Pavel Machek
2006-01-05 0:55 ` Pavel Machek
2006-01-05 23:34 ` Rafael J. Wysocki
2 siblings, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2006-01-05 0:54 UTC (permalink / raw)
To: Greg KH; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 1593 bytes --]
On St 04-01-06 16:26:19, Greg KH wrote:
> On Thu, Jan 05, 2006 at 01:18:37AM +0100, Pavel Machek wrote:
> > Hi!
> >
> > > > +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);
> > >
> > > Heh, that's a neat hack, register a sysfs file that contains the
> > > major:minor (there is a function that will print that the correct way,
> > > if you really want to do that), in sysfs. It's better to just register
> > > a misc character device with the name "snapshot", and then udev will
> > > create your userspace node with the proper major:minor all automatically
> > > for you.
> > >
> > > Unless you want to turn these into syscalls :)
> >
> > Well, I think we simply want to get static major/minor allocated for
> > this device. It really uses read/write, IIRC, so no, I do not think we
> > want to make it a syscall.
>
> Ok, then I'd recommend using the misc device, dynamic for now, and
> reserve one when you get a bit closer to merging into mainline.
I'd say that "memory devices", character, major 1 is appropriate for
this. 13 seems to be free ("swsusp can be unlucky for your data"? :-).
Pavel
--
Thanks, Sharp!
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-05 0:26 ` Greg KH
2006-01-05 0:54 ` Pavel Machek
@ 2006-01-05 0:55 ` Pavel Machek
2006-01-05 23:34 ` Rafael J. Wysocki
2 siblings, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2006-01-05 0:55 UTC (permalink / raw)
To: Greg KH; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 624 bytes --]
Hi!
> > > Unless you want to turn these into syscalls :)
> >
> > Well, I think we simply want to get static major/minor allocated for
> > this device. It really uses read/write, IIRC, so no, I do not think we
> > want to make it a syscall.
>
> Ok, then I'd recommend using the misc device, dynamic for now, and
> reserve one when you get a bit closer to merging into mainline.
Actually, misc major (character, major 10) seems to be okay,
too. There's stuff like /dev/fuse there, already. But it feels little
crowded there, and /dev/snapshot seems to fit between memory devices
nicely...
Pavel
--
Thanks, Sharp!
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
` (4 preceding siblings ...)
2006-01-04 22:56 ` [RFC/RFT][PATCH -mm 5/5] swsusp: userland interface documentation and config Rafael J. Wysocki
@ 2006-01-05 1:05 ` Pavel Machek
2006-01-05 23:30 ` Pavel Machek
6 siblings, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2006-01-05 1:05 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM, LKML, Nigel Cunningham, ast
Hi!
> This is the second "preview release" of the swsusp userland interface patches.
> They have changed quite a bit since the previous post, as I tried to make the
> interface more robust against some potential user space bugs (or outright
> attempts to abuse it).
>
> The swsusp userland 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.
...
> Any feedback will be very much appreciated.
Pretty please, give it a try. If you think about introducing
splashscreen/compression/encryption into swsusp, userland parts of
these patches should be great place for it.
Pavel
--
Thanks, Sharp!
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
` (5 preceding siblings ...)
2006-01-05 1:05 ` [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Pavel Machek
@ 2006-01-05 23:30 ` Pavel Machek
2006-01-06 21:17 ` Rafael J. Wysocki
6 siblings, 1 reply; 19+ messages in thread
From: Pavel Machek @ 2006-01-05 23:30 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM, LKML
Hi!
> This is the second "preview release" of the swsusp userland interface patches.
> They have changed quite a bit since the previous post, as I tried to make the
> interface more robust against some potential user space bugs (or outright
> attempts to abuse it).
Works for me, thanks.
Perhaps it is time to get 1/4 and 3/4 into -mm? You get my signed-off
on them...
2/4 needs to allocate official major/minor. 1/13 would be nice :-).
4/4... I'm not sure. It would be nice to make swsusp.c disappear. It
is really wrong name. That means we need to only delete from it for a
while...
Pavel
--
Thanks, Sharp!
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-05 0:26 ` Greg KH
2006-01-05 0:54 ` Pavel Machek
2006-01-05 0:55 ` Pavel Machek
@ 2006-01-05 23:34 ` Rafael J. Wysocki
2006-01-05 23:45 ` Greg KH
2 siblings, 1 reply; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-05 23:34 UTC (permalink / raw)
To: Greg KH; +Cc: Linux PM, LKML, Pavel Machek
Hi,
On Thursday, 5 January 2006 01:26, Greg KH wrote:
> On Thu, Jan 05, 2006 at 01:18:37AM +0100, Pavel Machek wrote:
> > > > +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);
> > >
> > > Heh, that's a neat hack, register a sysfs file that contains the
> > > major:minor (there is a function that will print that the correct way,
> > > if you really want to do that), in sysfs. It's better to just register
> > > a misc character device with the name "snapshot", and then udev will
> > > create your userspace node with the proper major:minor all automatically
> > > for you.
> > >
> > > Unless you want to turn these into syscalls :)
> >
> > Well, I think we simply want to get static major/minor allocated for
> > this device. It really uses read/write, IIRC, so no, I do not think we
> > want to make it a syscall.
>
> Ok, then I'd recommend using the misc device, dynamic for now, and
> reserve one when you get a bit closer to merging into mainline.
Do you mean something like in the appended patch?
Rafael
Index: linux-2.6.15-mm1/kernel/power/user.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15-mm1/kernel/power/user.c 2006-01-06 00:22:52.000000000 +0100
@@ -0,0 +1,294 @@
+/*
+ * 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/miscdevice.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"
+
+static struct snapshot_data {
+ struct snapshot_handle handle;
+ int swap;
+ struct bitmap_page *bitmap;
+ int mode;
+ char frozen;
+ char ready;
+} snapshot_state;
+
+static atomic_t device_available = ATOMIC_INIT(1);
+
+int snapshot_open(struct inode *inode, struct file *filp)
+{
+ struct snapshot_data *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 = &snapshot_state;
+ 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->bitmap = NULL;
+ data->frozen = 0;
+ data->ready = 0;
+
+ return 0;
+}
+
+int snapshot_release(struct inode *inode, struct file *filp)
+{
+ struct snapshot_data *data;
+
+ swsusp_free();
+ data = filp->private_data;
+ free_all_swap_pages(data->swap, data->bitmap);
+ free_bitmap(data->bitmap);
+ if (data->frozen) {
+ down(&pm_sem);
+ thaw_processes();
+ enable_nonboot_cpus();
+ up(&pm_sem);
+ }
+ 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_data *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_data *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_data *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:
+ if (data->frozen)
+ break;
+ 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:
+ if (!data->frozen)
+ break;
+ 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 || data->ready) {
+ 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);
+ if (!error)
+ data->ready = 1;
+ break;
+
+ case SNAPSHOT_IOCATOMIC_RESTORE:
+ if (data->mode != O_WRONLY || !data->frozen ||
+ !snapshot_image_loaded(&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_IOCFREE:
+ swsusp_free();
+ memset(&data->handle, 0, sizeof(struct snapshot_handle));
+ data->ready = 0;
+ 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 (!access_ok(VERIFY_WRITE, (unsigned long __user *)arg, _IOC_SIZE(cmd))) {
+ error = -EINVAL;
+ break;
+ }
+ if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
+ error = -ENODEV;
+ break;
+ }
+ if (!data->bitmap) {
+ data->bitmap = alloc_bitmap(swsusp_total_swap(data->swap));
+ if (!data->bitmap) {
+ error = -ENOMEM;
+ break;
+ }
+ }
+ offset = alloc_swap_page(data->swap, data->bitmap);
+ if (offset)
+ __put_user(offset, (unsigned long __user *)arg);
+ else
+ error = -ENOSPC;
+ break;
+
+ case SNAPSHOT_IOCFREE_SWAP_PAGES:
+ if (data->swap >= 0 && data->swap < MAX_SWAPFILES) {
+ error = -ENODEV;
+ break;
+ }
+ free_all_swap_pages(data->swap, data->bitmap);
+ free_bitmap(data->bitmap);
+ data->bitmap = NULL;
+ break;
+
+ case SNAPSHOT_IOCSET_SWAP_FILE:
+ if (!data->bitmap) {
+ /*
+ * User space encodes device types as two-byte values,
+ * so we need to recode them
+ */
+ data->swap = swsusp_get_swap_index_of(old_decode_dev(arg));
+ if (data->swap < 0)
+ error = -ENODEV;
+ } else {
+ error = -EPERM;
+ }
+ 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 miscdevice snapshot_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "snapshot",
+ .fops = &snapshot_fops,
+};
+
+static int __init snapshot_device_init(void)
+{
+ return misc_register(&snapshot_device);
+};
+
+device_initcall(snapshot_device_init);
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 2/5] swsusp: userland interface (rev. 2)
2006-01-05 23:34 ` Rafael J. Wysocki
@ 2006-01-05 23:45 ` Greg KH
0 siblings, 0 replies; 19+ messages in thread
From: Greg KH @ 2006-01-05 23:45 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM, LKML, Pavel Machek
[-- Attachment #1: Type: text/plain, Size: 309 bytes --]
On Fri, Jan 06, 2006 at 12:34:52AM +0100, Rafael J. Wysocki wrote:
> > Ok, then I'd recommend using the misc device, dynamic for now, and
> > reserve one when you get a bit closer to merging into mainline.
>
> Do you mean something like in the appended patch?
Yes, that looks good to me.
thanks,
greg k-h
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
2006-01-05 23:30 ` Pavel Machek
@ 2006-01-06 21:17 ` Rafael J. Wysocki
2006-01-06 22:44 ` Pavel Machek
0 siblings, 1 reply; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-06 21:17 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
Hi,
On Friday, 6 January 2006 00:30, Pavel Machek wrote:
> > This is the second "preview release" of the swsusp userland interface patches.
> > They have changed quite a bit since the previous post, as I tried to make the
> > interface more robust against some potential user space bugs (or outright
> > attempts to abuse it).
>
> Works for me, thanks.
>
> Perhaps it is time to get 1/4 and 3/4 into -mm? You get my signed-off
> on them...
OK, I'll prepare them in a while.
> 2/4 needs to allocate official major/minor. 1/13 would be nice :-).
Well, you said you liked the patch with a misc device (ie. major = 10).
Actually the code is somewhat simpler in that case so I'd prefer it.
Now, if we used a misc device, which minor would be suitable? 231?
> 4/4... I'm not sure. It would be nice to make swsusp.c disappear. It
> is really wrong name. That means we need to only delete from it for a
> while...
Anyway I think it would be nice to move the code that does not really belong
to the snapshot and is used by both the user interface and disk.c/swap.c to
a separate file. I have no preference as far as the name of the file is
concerned, though.
Greetings,
Rafael
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
2006-01-06 21:17 ` Rafael J. Wysocki
@ 2006-01-06 22:44 ` Pavel Machek
2006-01-06 23:41 ` Rafael J. Wysocki
0 siblings, 1 reply; 19+ messages in thread
From: Pavel Machek @ 2006-01-06 22:44 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM, LKML
[-- Attachment #1: Type: text/plain, Size: 1593 bytes --]
Hi!
> > > This is the second "preview release" of the swsusp userland interface patches.
> > > They have changed quite a bit since the previous post, as I tried to make the
> > > interface more robust against some potential user space bugs (or outright
> > > attempts to abuse it).
> >
> > Works for me, thanks.
> >
> > Perhaps it is time to get 1/4 and 3/4 into -mm? You get my signed-off
> > on them...
>
> OK, I'll prepare them in a while.
Thanks.
> > 2/4 needs to allocate official major/minor. 1/13 would be nice :-).
>
> Well, you said you liked the patch with a misc device (ie. major = 10).
>
> Actually the code is somewhat simpler in that case so I'd prefer it.
>
> Now, if we used a misc device, which minor would be suitable? 231?
If code is simpler, lets stick with misc. You have to obtain minor by
mailing device@lanana.org, see Doc*/devices.txt.
> > 4/4... I'm not sure. It would be nice to make swsusp.c disappear. It
> > is really wrong name. That means we need to only delete from it for a
> > while...
>
> Anyway I think it would be nice to move the code that does not really belong
> to the snapshot and is used by both the user interface and disk.c/swap.c to
> a separate file. I have no preference as far as the name of the file is
> concerned, though.
Ok, lets keep it as it is. We can always rename file in future. [I
don't quite understand your reasons for movement, through. Highmem is
part of snapshot we need to make; it is saved in a very different way
than rest of memory, but that is implementation detail...]
Pavel
--
Thanks, Sharp!
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
2006-01-06 22:44 ` Pavel Machek
@ 2006-01-06 23:41 ` Rafael J. Wysocki
2006-01-06 23:59 ` Pavel Machek
0 siblings, 1 reply; 19+ messages in thread
From: Rafael J. Wysocki @ 2006-01-06 23:41 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM, LKML
Hi,
On Friday, 6 January 2006 23:44, Pavel Machek wrote:
> > > > This is the second "preview release" of the swsusp userland interface patches.
> > > > They have changed quite a bit since the previous post, as I tried to make the
> > > > interface more robust against some potential user space bugs (or outright
> > > > attempts to abuse it).
> > >
> > > Works for me, thanks.
> > >
> > > Perhaps it is time to get 1/4 and 3/4 into -mm? You get my signed-off
> > > on them...
> >
> > OK, I'll prepare them in a while.
>
> Thanks.
I had to remake the 3/4 a bit, as it depended on some changes to power.h
and swsusp.c done in the 2/4. Nothing particularly invasive, basically some
definitions go to power.h and some function headers change, but please
have a look if the patch is still OK (appended).
> > > 2/4 needs to allocate official major/minor. 1/13 would be nice :-).
> >
> > Well, you said you liked the patch with a misc device (ie. major = 10).
> >
> > Actually the code is somewhat simpler in that case so I'd prefer it.
> >
> > Now, if we used a misc device, which minor would be suitable? 231?
>
> If code is simpler, lets stick with misc. You have to obtain minor by
> mailing device@lanana.org, see Doc*/devices.txt.
OK, I'll try.
> > > 4/4... I'm not sure. It would be nice to make swsusp.c disappear. It
> > > is really wrong name. That means we need to only delete from it for a
> > > while...
> >
> > Anyway I think it would be nice to move the code that does not really belong
> > to the snapshot and is used by both the user interface and disk.c/swap.c to
> > a separate file. I have no preference as far as the name of the file is
> > concerned, though.
>
> Ok, lets keep it as it is. We can always rename file in future. [I
> don't quite understand your reasons for movement, through. Highmem is
> part of snapshot we need to make; it is saved in a very different way
> than rest of memory, but that is implementation detail...]
I'm seeing this a bit differently. In my view highmem is handled very much
like devices: save_highmem() turns it "off", restore_highmem() turns it "on"
back again, they are even called next to device_power_up/down().
Besides, the highmem-related code is only invoked from
swsusp_suspend/resume() and swsusp_shrink_memory(), so in swsusp.c it
can be made static or even static inline (eg. save_highmem() is called only
once, and so is count_highmem_pages()).
Greetings,
Rafael
kernel/power/Makefile | 2
kernel/power/power.h | 35 ++
kernel/power/swap.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++
kernel/power/swsusp.c | 596 ++------------------------------------------------
4 files changed, 617 insertions(+), 572 deletions(-)
Index: linux-2.6.15-mm1/kernel/power/Makefile
===================================================================
--- linux-2.6.15-mm1.orig/kernel/power/Makefile 2006-01-06 23:23:05.000000000 +0100
+++ linux-2.6.15-mm1/kernel/power/Makefile 2006-01-06 23:27:19.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 swap.o
obj-$(CONFIG_SUSPEND_SMP) += smp.o
Index: linux-2.6.15-mm1/kernel/power/power.h
===================================================================
--- linux-2.6.15-mm1.orig/kernel/power/power.h 2006-01-06 23:24:18.000000000 +0100
+++ linux-2.6.15-mm1/kernel/power/power.h 2006-01-06 23:29:36.000000000 +0100
@@ -53,8 +53,8 @@ extern struct pbe *pagedir_nosave;
/* Preferred image size in MB (default 500) */
extern unsigned int image_size;
-
extern int in_suspend;
+extern dev_t swsusp_resume_device;
extern asmlinkage int swsusp_arch_suspend(void);
extern asmlinkage int swsusp_arch_resume(void);
@@ -77,3 +77,36 @@ 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_loaded(struct snapshot_handle *handle);
+
+/**
+ * The bitmap is used for tracing allocated swap pages
+ *
+ * The entire bitmap consists of a number of bitmap_page
+ * structures linked with the help of the .next member.
+ * Thus each page can be allocated individually, so we only
+ * need to make 0-order memory allocations to create
+ * the bitmap.
+ */
+
+#define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *))
+#define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long))
+#define BITS_PER_CHUNK (sizeof(long) * 8)
+#define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK)
+
+struct bitmap_page {
+ unsigned long chunks[BITMAP_PAGE_CHUNKS];
+ struct bitmap_page *next;
+};
+
+extern void free_bitmap(struct bitmap_page *bitmap);
+extern struct bitmap_page *alloc_bitmap(unsigned int nr_bits);
+extern unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap);
+extern void free_all_swap_pages(int swap, struct bitmap_page *bitmap);
+
+extern int swsusp_get_swap_index_of(dev_t device);
+extern int swsusp_get_swap_index(void);
+extern unsigned int swsusp_total_swap(unsigned int swap);
+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);
Index: linux-2.6.15-mm1/kernel/power/swap.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15-mm1/kernel/power/swap.c 2006-01-06 23:27:19.000000000 +0100
@@ -0,0 +1,556 @@
+/*
+ * 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 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;
+
+/**
+ * mark_swapfiles - change the signature of the resume partition
+ * and store the swap address of given entry in the partition's
+ * header (the entry should point to the first image page in the
+ * swap)
+ */
+
+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;
+}
+
+/**
+ * swsusp_swap_check - check if the resume partition is available
+ */
+
+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 given swap location.
+ * @buf: Address we're writing.
+ * @offset: Offset of the swap page we're writing to.
+ */
+
+static int write_page(void *buf, unsigned long offset)
+{
+ swp_entry_t entry;
+ int error = -ENOSPC;
+
+ if (offset) {
+ entry = swp_entry(root_swap, offset);
+ error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
+ }
+ return error;
+}
+
+/*
+ * The swap map is a data structure used for keeping track of each page
+ * written to a swap partition. It consists of many swap_map_page
+ * structures that contain each an array of MAP_PAGE_SIZE swap entries.
+ * These structures are stored on the swap and linked together with the
+ * help of the .next_swap member.
+ *
+ * The swap map is created during suspend. The swap map pages are
+ * allocated and populated one at a time, so we only need one memory
+ * page to set up the entire structure.
+ *
+ * During resume we also only need to use one swap_map_page structure
+ * at a time.
+ */
+
+#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1)
+
+struct swap_map_page {
+ unsigned long entries[MAP_PAGE_ENTRIES];
+ unsigned long next_swap;
+};
+
+/**
+ * The swap_map_handle structure is used for handling swap in
+ * a file-alike way
+ */
+
+struct swap_map_handle {
+ struct swap_map_page *cur;
+ unsigned long cur_swap;
+ struct bitmap_page *bitmap;
+ unsigned int k;
+};
+
+static inline void release_swap_writer(struct swap_map_handle *handle)
+{
+ if (handle->cur)
+ free_page((unsigned long)handle->cur);
+ handle->cur = NULL;
+ if (handle->bitmap)
+ free_bitmap(handle->bitmap);
+ handle->bitmap = NULL;
+}
+
+static inline int get_swap_writer(struct swap_map_handle *handle)
+{
+ handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
+ if (!handle->cur)
+ return -ENOMEM;
+ handle->bitmap = alloc_bitmap(swsusp_total_swap(root_swap));
+ if (!handle->bitmap) {
+ release_swap_writer(handle);
+ return -ENOMEM;
+ }
+ handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap);
+ if (!handle->cur_swap) {
+ release_swap_writer(handle);
+ return -ENOSPC;
+ }
+ handle->k = 0;
+ return 0;
+}
+
+static inline int swap_write_page(struct swap_map_handle *handle, void *buf)
+{
+ int error;
+ unsigned long offset;
+
+ if (!handle->cur)
+ return -EINVAL;
+ offset = alloc_swap_page(root_swap, handle->bitmap);
+ error = write_page(buf, offset);
+ if (error)
+ return error;
+ handle->cur->entries[handle->k++] = offset;
+ if (handle->k >= MAP_PAGE_ENTRIES) {
+ offset = alloc_swap_page(root_swap, handle->bitmap);
+ if (!offset)
+ return -ENOSPC;
+ handle->cur->next_swap = offset;
+ error = write_page(handle->cur, handle->cur_swap);
+ if (error)
+ return error;
+ memset(handle->cur, 0, PAGE_SIZE);
+ handle->cur_swap = offset;
+ handle->k = 0;
+ }
+ return 0;
+}
+
+static inline int flush_swap_writer(struct swap_map_handle *handle)
+{
+ if (handle->cur && handle->cur_swap)
+ return write_page(handle->cur, handle->cur_swap);
+ else
+ return -EINVAL;
+}
+
+/**
+ * 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_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_handle handle;
+ struct snapshot_handle snapshot;
+ struct swsusp_info *header;
+ unsigned long 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);
+ if (!enough_swap(header->pages)) {
+ printk(KERN_ERR "swsusp: Not enough free swap\n");
+ return -ENOSPC;
+ }
+ error = get_swap_writer(&handle);
+ if (!error) {
+ start = handle.cur_swap;
+ error = swap_write_page(&handle, header);
+ }
+ if (!error)
+ error = save_image(&handle, &snapshot, header->pages - 1);
+ if (!error) {
+ flush_swap_writer(&handle);
+ printk("S");
+ error = mark_swapfiles(swp_entry(root_swap, start));
+ printk("|\n");
+ }
+ if (error)
+ free_all_swap_pages(root_swap, handle.bitmap);
+ release_swap_writer(&handle);
+ 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_reader(struct swap_map_handle *handle)
+{
+ if (handle->cur)
+ free_page((unsigned long)handle->cur);
+ handle->cur = NULL;
+}
+
+static inline int get_swap_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_reader(handle);
+ return error;
+ }
+ handle->k = 0;
+ return 0;
+}
+
+static inline int swap_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 = bio_read_page(offset, buf);
+ if (error)
+ return error;
+ if (++handle->k >= MAP_PAGE_ENTRIES) {
+ handle->k = 0;
+ offset = handle->cur->next_swap;
+ if (!offset)
+ release_swap_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_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_loaded(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_reader(&handle, swsusp_header.image);
+ if (!error)
+ error = swap_read_page(&handle, header);
+ if (!error) {
+ nr_pages = header->image_pages;
+ error = load_image(&handle, &snapshot, nr_pages);
+ }
+ release_swap_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-mm1/kernel/power/swsusp.c
===================================================================
--- linux-2.6.15-mm1.orig/kernel/power/swsusp.c 2006-01-06 23:24:18.000000000 +0100
+++ linux-2.6.15-mm1/kernel/power/swsusp.c 2006-01-06 23:27:19.000000000 +0100
@@ -31,41 +31,24 @@
* Fixed runaway init
*
* Rafael J. Wysocki <rjw@sisk.pl>
- * Added the swap map data structure and reworked the handling of swap
+ * Reworked the handling of swap and added the user interface
*
* More state savers are welcome. Especially for the scsi layer...
*
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
*/
-#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
-#include <linux/smp_lock.h>
-#include <linux/file.h>
-#include <linux/utsname.h>
-#include <linux/version.h>
-#include <linux/delay.h>
-#include <linux/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,82 +72,42 @@ 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.
+ * is_device - check whether the specified device is a swap device,
+ * irrespective of whether they are specified by identical names.
*
- * (Thus, device inode aliasing is allowed. You can say /dev/hda4
- * instead of /dev/ide/host0/bus0/target0/lun0/part4 [eg. for devfs]
- * and they'll be considered the same device. This was *necessary* for
- * devfs, since the resume code could only recognize the form /dev/hda4,
- * but the suspend code would see the long name.)
+ * (Thus, device inode aliasing is allowed. You can say /dev/hda4
+ * instead of /dev/ide/host0/bus0/target0/lun0/part4 [eg. for devfs]
+ * and they'll be considered the same device. This was *necessary* for
+ * devfs, since the resume code could 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)
+static inline int is_device(const struct swap_info_struct *swap_info,
+ dev_t device)
{
struct file *file = swap_info->swap_file;
struct inode *inode = file->f_dentry->d_inode;
return S_ISBLK(inode->i_mode) &&
- swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
+ device == MKDEV(imajor(inode), iminor(inode));
}
/**
- * swsusp_get_swap_index - find the index of the resume device
+ * swsusp_get_swap_index_of - find the index of the given device
*/
-int swsusp_get_swap_index(void)
+int swsusp_get_swap_index_of(dev_t device)
{
int i;
- if (!swsusp_resume_device)
- return -ENODEV;
+ if (!device)
+ return -EINVAL;
spin_lock(&swap_lock);
for (i = 0; i < MAX_SWAPFILES; i++) {
if (!(swap_info[i].flags & SWP_WRITEOK))
continue;
- if (is_resume_device(swap_info + i)) {
+ if (is_device(swap_info + i, device)) {
spin_unlock(&swap_lock);
return i;
}
@@ -173,46 +116,24 @@ 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;
-}
-
/**
- * The bitmap is used for tracing allocated swap pages
- *
- * The entire bitmap consists of a number of bitmap_page
- * structures linked with the help of the .next member.
- * Thus each page can be allocated individually, so we only
- * need to make 0-order memory allocations to create
- * the bitmap.
+ * swsusp_get_swap_index - find the index of the resume device
*/
-#define BITMAP_PAGE_SIZE (PAGE_SIZE - sizeof(void *))
-#define BITMAP_PAGE_CHUNKS (BITMAP_PAGE_SIZE / sizeof(long))
-#define BITS_PER_CHUNK (sizeof(long) * 8)
-#define BITMAP_PAGE_BITS (BITMAP_PAGE_CHUNKS * BITS_PER_CHUNK)
-
-struct bitmap_page {
- unsigned long chunks[BITMAP_PAGE_CHUNKS];
- struct bitmap_page *next;
-};
+int swsusp_get_swap_index(void)
+{
+ return swsusp_get_swap_index_of(swsusp_resume_device);
+}
/**
* The following functions are used for tracing the allocated
* swap pages, so that they can be freed in case of an error.
*
* The functions operate on a linked bitmap structure defined
- * above
+ * in power.h
*/
-static void free_bitmap(struct bitmap_page *bitmap)
+void free_bitmap(struct bitmap_page *bitmap)
{
struct bitmap_page *bp;
@@ -223,7 +144,7 @@ static void free_bitmap(struct bitmap_pa
}
}
-static struct bitmap_page *alloc_bitmap(unsigned int nr_bits)
+struct bitmap_page *alloc_bitmap(unsigned int nr_bits)
{
struct bitmap_page *bitmap, *bp;
unsigned int n;
@@ -266,7 +187,7 @@ static inline int bitmap_set(struct bitm
return 0;
}
-static unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap)
+unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap)
{
unsigned long offset;
@@ -280,7 +201,7 @@ static unsigned long alloc_swap_page(int
return offset;
}
-static void free_all_swap_pages(int swap, struct bitmap_page *bitmap)
+void free_all_swap_pages(int swap, struct bitmap_page *bitmap)
{
unsigned int bit, n;
unsigned long test;
@@ -298,24 +219,6 @@ static void free_all_swap_pages(int swap
}
/**
- * write_page - Write one page to given swap location.
- * @buf: Address we're writing.
- * @offset: Offset of the swap page we're writing to.
- */
-
-static int write_page(void *buf, unsigned long offset)
-{
- swp_entry_t entry;
- int error = -ENOSPC;
-
- if (offset) {
- entry = swp_entry(root_swap, offset);
- error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf));
- }
- return error;
-}
-
-/**
* swsusp_total_swap - check the total amount of swap
* space in the resume partition/file
*/
@@ -333,137 +236,6 @@ unsigned int swsusp_total_swap(unsigned
return n;
}
-/*
- * The swap map is a data structure used for keeping track of each page
- * written to a swap partition. It consists of many swap_map_page
- * structures that contain each an array of MAP_PAGE_SIZE swap entries.
- * These structures are stored on the swap and linked together with the
- * help of the .next_swap member.
- *
- * The swap map is created during suspend. The swap map pages are
- * allocated and populated one at a time, so we only need one memory
- * page to set up the entire structure.
- *
- * During resume we also only need to use one swap_map_page structure
- * at a time.
- */
-
-#define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(long) - 1)
-
-struct swap_map_page {
- unsigned long entries[MAP_PAGE_ENTRIES];
- unsigned long next_swap;
-};
-
-/**
- * The swap_map_handle structure is used for handling swap in
- * a file-alike way
- */
-
-struct swap_map_handle {
- struct swap_map_page *cur;
- unsigned long cur_swap;
- struct bitmap_page *bitmap;
- unsigned int k;
-};
-
-static inline void release_swap_writer(struct swap_map_handle *handle)
-{
- if (handle->cur)
- free_page((unsigned long)handle->cur);
- handle->cur = NULL;
- if (handle->bitmap)
- free_bitmap(handle->bitmap);
- handle->bitmap = NULL;
-}
-
-static inline int get_swap_writer(struct swap_map_handle *handle)
-{
- handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
- if (!handle->cur)
- return -ENOMEM;
- handle->bitmap = alloc_bitmap(swsusp_total_swap(root_swap));
- if (!handle->bitmap) {
- release_swap_writer(handle);
- return -ENOMEM;
- }
- handle->cur_swap = alloc_swap_page(root_swap, handle->bitmap);
- if (!handle->cur_swap) {
- release_swap_writer(handle);
- return -ENOSPC;
- }
- handle->k = 0;
- return 0;
-}
-
-static inline int swap_write_page(struct swap_map_handle *handle, void *buf)
-{
- int error;
- unsigned long offset;
-
- if (!handle->cur)
- return -EINVAL;
- offset = alloc_swap_page(root_swap, handle->bitmap);
- error = write_page(buf, offset);
- if (error)
- return error;
- handle->cur->entries[handle->k++] = offset;
- if (handle->k >= MAP_PAGE_ENTRIES) {
- offset = alloc_swap_page(root_swap, handle->bitmap);
- if (!offset)
- return -ENOSPC;
- handle->cur->next_swap = offset;
- error = write_page(handle->cur, handle->cur_swap);
- if (error)
- return error;
- memset(handle->cur, 0, PAGE_SIZE);
- handle->cur_swap = offset;
- handle->k = 0;
- }
- return 0;
-}
-
-static inline int flush_swap_writer(struct swap_map_handle *handle)
-{
- if (handle->cur && handle->cur_swap)
- return write_page(handle->cur, handle->cur_swap);
- else
- return -EINVAL;
-}
-
-/**
- * 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_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
@@ -484,71 +256,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_handle handle;
- struct snapshot_handle snapshot;
- struct swsusp_info *header;
- unsigned long 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);
- if (!enough_swap(header->pages)) {
- printk(KERN_ERR "swsusp: Not enough free swap\n");
- return -ENOSPC;
- }
- error = get_swap_writer(&handle);
- if (!error) {
- start = handle.cur_swap;
- error = swap_write_page(&handle, header);
- }
- if (!error)
- error = save_image(&handle, &snapshot, header->pages - 1);
- if (!error) {
- flush_swap_writer(&handle);
- printk("S");
- error = mark_swapfiles(swp_entry(root_swap, start));
- printk("|\n");
- }
- if (error)
- free_all_swap_pages(root_swap, handle.bitmap);
- release_swap_writer(&handle);
- return error;
-}
-
-/**
* swsusp_shrink_memory - Try to free as much memory as needed
*
* ... but do not OOM-kill anyone
@@ -654,254 +361,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_reader(struct swap_map_handle *handle)
-{
- if (handle->cur)
- free_page((unsigned long)handle->cur);
- handle->cur = NULL;
-}
-
-static inline int get_swap_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_reader(handle);
- return error;
- }
- handle->k = 0;
- return 0;
-}
-
-static inline int swap_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 = bio_read_page(offset, buf);
- if (error)
- return error;
- if (++handle->k >= MAP_PAGE_ENTRIES) {
- handle->k = 0;
- offset = handle->cur->next_swap;
- if (!offset)
- release_swap_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_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_loaded(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_reader(&handle, swsusp_header.image);
- if (!error)
- error = swap_read_page(&handle, header);
- if (!error) {
- nr_pages = header->image_pages;
- error = load_image(&handle, &snapshot, nr_pages);
- }
- release_swap_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);
-}
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2)
2006-01-06 23:41 ` Rafael J. Wysocki
@ 2006-01-06 23:59 ` Pavel Machek
0 siblings, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2006-01-06 23:59 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM, LKML
Hi!
> > > > Perhaps it is time to get 1/4 and 3/4 into -mm? You get my signed-off
> > > > on them...
> > >
> > > OK, I'll prepare them in a while.
> >
> > Thanks.
>
> I had to remake the 3/4 a bit, as it depended on some changes to power.h
> and swsusp.c done in the 2/4. Nothing particularly invasive, basically some
> definitions go to power.h and some function headers change, but please
> have a look if the patch is still OK (appended).
Still ok :-).
> > If code is simpler, lets stick with misc. You have to obtain minor by
> > mailing device@lanana.org, see Doc*/devices.txt.
>
> OK, I'll try.
It should be easy.
> > Ok, lets keep it as it is. We can always rename file in future. [I
> > don't quite understand your reasons for movement, through. Highmem is
> > part of snapshot we need to make; it is saved in a very different way
> > than rest of memory, but that is implementation detail...]
>
> I'm seeing this a bit differently. In my view highmem is handled very much
> like devices: save_highmem() turns it "off", restore_highmem() turns it "on"
> back again, they are even called next to device_power_up/down().
Yes, I guess it is possible to view it like that, too.
BTW your "write 500MB to swap" hit mainline few hours
ago. Congratulations.
Pavel
--
Thanks, Sharp!
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2006-01-06 23:59 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-01-04 22:40 [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Rafael J. Wysocki
2006-01-04 22:47 ` [RFC/RFT][PATCH -mm 1/5] swsusp: swsusp: low level " Rafael J. Wysocki
2006-01-04 22:51 ` [RFC/RFT][PATCH -mm 2/5] swsusp: userland " Rafael J. Wysocki
2006-01-04 23:49 ` [linux-pm] " Greg KH
2006-01-05 0:18 ` Pavel Machek
2006-01-05 0:26 ` Greg KH
2006-01-05 0:54 ` Pavel Machek
2006-01-05 0:55 ` Pavel Machek
2006-01-05 23:34 ` Rafael J. Wysocki
2006-01-05 23:45 ` Greg KH
2006-01-04 22:53 ` [RFC/RFT][PATCH -mm 3/5] swsusp: separate swap-writing and reading code " Rafael J. Wysocki
2006-01-04 22:55 ` [RFC/RFT][PATCH -mm 4/5] swsusp: move highmem-handling code to swsusp.c " Rafael J. Wysocki
2006-01-04 22:56 ` [RFC/RFT][PATCH -mm 5/5] swsusp: userland interface documentation and config Rafael J. Wysocki
2006-01-05 1:05 ` [RFC/RFT][PATCH -mm 0/5] swsusp: userland interface (rev. 2) Pavel Machek
2006-01-05 23:30 ` Pavel Machek
2006-01-06 21:17 ` Rafael J. Wysocki
2006-01-06 22:44 ` Pavel Machek
2006-01-06 23:41 ` Rafael J. Wysocki
2006-01-06 23:59 ` Pavel Machek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox