summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2003-09-01 19:57:13 -0700
committerPatrick Mochel <mochel@osdl.org>2003-09-01 19:57:13 -0700
commitf1a6400876c588497a5477cd642ead33fcbc7e81 (patch)
tree587d76fb4bded89975b4d0fe1ce74d2d3b218dec
parent308f1c41c58a2a43023ddf54d0df6f35a9674232 (diff)
parentd5bd8347829ab21cdbce604c904350b42168a81a (diff)
Merge bk://kernel.bkbits.net//home/mochel/linux-2.5-power
into osdl.org:/home/mochel/src/kernel/linux-2.5-power
-rw-r--r--drivers/acpi/sleep/main.c21
-rw-r--r--drivers/acpi/sleep/proc.c73
-rw-r--r--drivers/acpi/sleep/sleep.h3
-rw-r--r--include/linux/suspend.h1
-rw-r--r--kernel/power/swsusp.c222
-rw-r--r--kernel/sys.c2
6 files changed, 249 insertions, 73 deletions
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 2fbbd80edaf7..1338cc96b724 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = {
static int acpi_pm_prepare(u32 state)
{
- int error = 0;
u32 acpi_state = acpi_suspend_states[state];
if (!sleep_states[acpi_state])
@@ -56,15 +55,9 @@ static int acpi_pm_prepare(u32 state)
acpi_set_firmware_waking_vector(
(acpi_physical_address) acpi_wakeup_address);
}
-
ACPI_FLUSH_CPU_CACHE();
-
acpi_enter_sleep_state_prep(acpi_state);
-
return 0;
- Err:
- acpi_set_firmware_waking_vector(0);
- return error;
}
@@ -153,6 +146,20 @@ static int acpi_pm_finish(u32 state)
}
+int acpi_suspend(u32 acpi_state)
+{
+ u32 states[] = {
+ [1] = PM_SUSPEND_STANDBY,
+ [3] = PM_SUSPEND_MEM,
+ [4] = PM_SUSPEND_DISK,
+ };
+
+ if (acpi_state <= 4 && states[acpi_state])
+ return pm_suspend(states[acpi_state]);
+ return -EINVAL;
+}
+
+
static struct pm_ops acpi_pm_ops = {
.prepare = acpi_pm_prepare,
.enter = acpi_pm_enter,
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c
index 41cbde00b785..4a15b0000aa8 100644
--- a/drivers/acpi/sleep/proc.c
+++ b/drivers/acpi/sleep/proc.c
@@ -13,12 +13,71 @@
#include "sleep.h"
+#define ACPI_SYSTEM_FILE_SLEEP "sleep"
#define ACPI_SYSTEM_FILE_ALARM "alarm"
#define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ("sleep")
+static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset)
+{
+ int i;
+
+ ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show");
+
+ for (i = 0; i <= ACPI_STATE_S5; i++) {
+ if (sleep_states[i]) {
+ seq_printf(seq,"S%d ", i);
+ if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f)
+ seq_printf(seq, "S4bios ");
+ }
+ }
+
+ seq_puts(seq, "\n");
+
+ return 0;
+}
+
+static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data);
+}
+
+static int
+acpi_system_write_sleep (
+ struct file *file,
+ const char *buffer,
+ size_t count,
+ loff_t *ppos)
+{
+ char str[12];
+ u32 state = 0;
+ int error = 0;
+
+ if (count > sizeof(str) - 1)
+ goto Done;
+ memset(str,0,sizeof(str));
+ if (copy_from_user(str, buffer, count))
+ return -EFAULT;
+
+ /* Check for S4 bios request */
+ if (!strcmp(str,"4b")) {
+ error = acpi_suspend(4);
+ goto Done;
+ }
+ state = simple_strtoul(str, NULL, 0);
+#ifdef CONFIG_SOFTWARE_SUSPEND
+ if (state == 4) {
+ error = software_suspend();
+ goto Done;
+ }
+#endif
+ error = acpi_suspend(state);
+ Done:
+ return error ? error : count;
+}
+
static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
{
u32 sec, min, hr;
@@ -294,6 +353,14 @@ end:
}
+static struct file_operations acpi_system_sleep_fops = {
+ .open = acpi_system_sleep_open_fs,
+ .read = seq_read,
+ .write = acpi_system_write_sleep,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static struct file_operations acpi_system_alarm_fops = {
.open = acpi_system_alarm_open_fs,
.read = seq_read,
@@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void)
{
struct proc_dir_entry *entry = NULL;
+ /* 'sleep' [R/W]*/
+ entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP,
+ S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
+ if (entry)
+ entry->proc_fops = &acpi_system_sleep_fops;
+
/* 'alarm' [R/W] */
entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM,
S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h
index 97b72323f35e..ad38f4153a34 100644
--- a/drivers/acpi/sleep/sleep.h
+++ b/drivers/acpi/sleep/sleep.h
@@ -1,5 +1,4 @@
extern u8 sleep_states[];
-
-extern acpi_status acpi_suspend (u32 state);
+extern int acpi_suspend (u32 state);
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 132db86c961a..ed8d796f1849 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -53,6 +53,7 @@ extern suspend_pagedir_t *pagedir_nosave __nosavedata;
extern void do_suspend_lowlevel(int resume);
extern void do_suspend_lowlevel_s4bios(int resume);
+extern int software_suspend(void);
#else /* CONFIG_SOFTWARE_SUSPEND */
static inline int software_suspend(void)
{
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index 7ae04d8eefac..c2c33c0cf76c 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -35,11 +35,11 @@
*/
#include <linux/mm.h>
+#include <linux/bio.h>
#include <linux/suspend.h>
#include <linux/version.h>
#include <linux/reboot.h>
#include <linux/device.h>
-#include <linux/buffer_head.h>
#include <linux/swapops.h>
#include <linux/bootmem.h>
@@ -673,61 +673,146 @@ static int __init sanity_check(struct suspend_header *sh)
return 0;
}
-static int __init bdev_read_page(struct block_device *bdev,
- long pos, void *buf)
+static struct block_device * resume_bdev;
+
+
+/**
+ * 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 void start_io(void)
{
- struct buffer_head *bh;
- BUG_ON (pos%PAGE_SIZE);
- bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
- if (!bh || (!bh->b_data)) {
- return -1;
- }
- memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */
- BUG_ON(!buffer_uptodate(bh));
- brelse(bh);
+ atomic_set(&io_done,1);
+}
+
+static int end_io(struct bio * bio, unsigned int num, int err)
+{
+ atomic_set(&io_done,0);
return 0;
-}
+}
+
+static void wait_io(void)
+{
+ blk_run_queues();
+ while(atomic_read(&io_done))
+ io_schedule();
+}
+
+
+/**
+ * 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("ERROR: adding page to bio at %ld\n",page_off);
+ error = -EFAULT;
+ goto Done;
+ }
+
+ if (rw == WRITE)
+ bio_set_pages_dirty(bio);
+ start_io();
+ submit_bio(rw,bio);
+ wait_io();
+ Done:
+ bio_put(bio);
+ return error;
+}
+
+static int
+read_page(pgoff_t page_off, void * page)
+{
+ return submit(READ,page_off,page);
+}
+
+static int
+write_page(pgoff_t page_off, void * page)
+{
+ return submit(WRITE,page_off,page);
+}
+
extern dev_t __init name_to_dev_t(const char *line);
-static int __init read_suspend_image(struct block_device *bdev,
- union diskpage *cur)
+
+#define next_entry(diskpage) diskpage->link.next
+
+static int __init read_suspend_image(void)
{
swp_entry_t next;
int i, nr_pgdir_pages;
+ union diskpage *cur;
+ int error = 0;
-#define PREPARENEXT \
- { next = cur->link.next; \
- next.val = swp_offset(next) * PAGE_SIZE; \
- }
-
- if (bdev_read_page(bdev, 0, cur)) return -EIO;
+ cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
+ if (!cur)
+ return -ENOMEM;
- if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
- (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
- printk(KERN_ERR "%sThis is normal swap space\n", name_resume );
- return -EINVAL;
- }
+ if ((error = read_page(0, cur)))
+ goto Done;
- PREPARENEXT; /* We have to read next position before we overwrite it */
+ /*
+ * We have to read next position before we overwrite it
+ */
+ next = next_entry(cur);
if (!memcmp("S1",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
else if (!memcmp("S2",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
- else {
- printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n",
- name_resume, cur->swh.magic.magic);
- return -EFAULT;
+ else if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
+ (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
+ printk(KERN_ERR "swsusp: Partition is normal swap space\n");
+ error = -EINVAL;
+ goto Done;
+ } else {
+ printk(KERN_ERR "swsusp: Invalid partition type.\n");
+ error = -EINVAL;
+ goto Done;
}
+ /*
+ * Reset swap signature now.
+ */
+ if ((error = write_page(0,cur)))
+ goto Done;
+
printk( "%sSignature found, resuming\n", name_resume );
MDELAY(1000);
- if (bdev_read_page(bdev, next.val, cur)) return -EIO;
- if (sanity_check(&cur->sh)) /* Is this same machine? */
- return -EPERM;
- PREPARENEXT;
+ if ((error = read_page(swp_offset(next), cur)))
+ goto Done;
+ /* Is this same machine? */
+ if ((error = sanity_check(&cur->sh)))
+ goto Done;
+ next = next_entry(cur);
pagedir_save = cur->sh.suspend_pagedir;
nr_copy_pages = cur->sh.num_pbes;
@@ -735,8 +820,10 @@ static int __init read_suspend_image(struct block_device *bdev,
pagedir_order = get_bitmask_order(nr_pgdir_pages);
pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
- if (!pagedir_nosave)
- return -ENOMEM;
+ if (!pagedir_nosave) {
+ error = -ENOMEM;
+ goto Done;
+ }
PRINTK( "%sReading pagedir, ", name_resume );
@@ -744,15 +831,17 @@ static int __init read_suspend_image(struct block_device *bdev,
for (i=nr_pgdir_pages-1; i>=0; i--) {
BUG_ON (!next.val);
cur = (union diskpage *)((char *) pagedir_nosave)+i;
- if (bdev_read_page(bdev, next.val, cur)) return -EIO;
- PREPARENEXT;
+ error = read_page(swp_offset(next), cur);
+ if (error)
+ goto FreePagedir;
+ next = next_entry(cur);
}
BUG_ON (next.val);
- if (relocate_pagedir())
- return -ENOMEM;
- if (check_pagedir())
- return -ENOMEM;
+ if ((error = relocate_pagedir()))
+ goto FreePagedir;
+ if ((error = check_pagedir()))
+ goto FreePagedir;
printk( "Reading image data (%d pages): ", nr_copy_pages );
for(i=0; i < nr_copy_pages; i++) {
@@ -761,11 +850,18 @@ static int __init read_suspend_image(struct block_device *bdev,
printk( "." );
/* You do not need to check for overlaps...
... check_pagedir already did this work */
- if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
- return -EIO;
+ error = read_page(swp_offset(swap_address),
+ (char *)((pagedir_nosave+i)->address));
+ if (error)
+ goto FreePagedir;
}
printk( "|\n" );
- return 0;
+ Done:
+ free_page((unsigned long)cur);
+ return error;
+ FreePagedir:
+ free_pages((unsigned long)pagedir_nosave,pagedir_order);
+ goto Done;
}
/**
@@ -804,7 +900,6 @@ int swsusp_write(void)
int __init swsusp_read(void)
{
- union diskpage *cur;
int error;
char b[BDEVNAME_SIZE];
@@ -815,19 +910,13 @@ int __init swsusp_read(void)
printk("swsusp: Resume From Partition: %s, Device: %s\n",
resume_file, __bdevname(resume_device, b));
- cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
- if (cur) {
- struct block_device *bdev;
- bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
- if (!IS_ERR(bdev)) {
- set_blocksize(bdev, PAGE_SIZE);
- error = read_suspend_image(bdev, cur);
- blkdev_put(bdev, BDEV_RAW);
- } else
- error = PTR_ERR(bdev);
- free_page((unsigned long)cur);
+ resume_bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
+ if (!IS_ERR(resume_bdev)) {
+ set_blocksize(resume_bdev, PAGE_SIZE);
+ error = read_suspend_image();
+ blkdev_put(resume_bdev, BDEV_RAW);
} else
- error = -ENOMEM;
+ error = PTR_ERR(resume_bdev);
if (!error)
PRINTK("Reading resume file was successful\n");
@@ -856,13 +945,20 @@ int swsusp_free(void)
{
PRINTK( "Freeing prev allocated pagedir\n" );
free_suspend_pagedir((unsigned long) pagedir_save);
-
- PRINTK( "Fixing swap signatures... " );
- mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
- PRINTK( "ok\n" );
return 0;
}
+
+int software_suspend(void)
+{
+ struct pm_ops swsusp_ops = {
+ .pm_disk_mode = PM_DISK_SHUTDOWN,
+ };
+
+ pm_set_ops(&swsusp_ops);
+ return pm_suspend(PM_SUSPEND_DISK);
+}
+
static int __init resume_setup(char *str)
{
if (strlen(str))
diff --git a/kernel/sys.c b/kernel/sys.c
index 02b5a12dfd59..d77453173d29 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -456,7 +456,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
- if (!pm_suspend(PM_SUSPEND_DISK))
+ if (!software_suspend())
break;
do_exit(0);
break;