aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2006-03-23 06:00:03 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-23 10:38:07 -0500
commit6e1819d615f24ce0726a7d0bd3dd0152d7b21654 (patch)
treeabc68747446e8241a1a7103882b9f6b6e24fa274
parent543cc27d09643640cbc34189c03a40beb8227aef (diff)
[PATCH] swsusp: userland interface
This patch introduces a user space interface for swsusp. The interface is based on a special character device, called the snapshot device, that allows 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 free swap pages from a selected swap partition, called the resume partition, so that they know which sectors of the resume partition are available to them. The interface uses the same low-level system memory snapshot-handling functions that are used by the built-it swap-writing/reading code of swsusp. The interface documentation is included in the patch. The patch assumes that the major and minor numbers of the snapshot device will be 10 (ie. misc device) and 231, the registration of which has already been requested. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--Documentation/power/userland-swsusp.txt149
-rw-r--r--init/do_mounts_initrd.c1
-rw-r--r--kernel/power/Makefile2
-rw-r--r--kernel/power/power.h14
-rw-r--r--kernel/power/snapshot.c9
-rw-r--r--kernel/power/user.c301
-rw-r--r--mm/swapfile.c6
7 files changed, 475 insertions, 7 deletions
diff --git a/Documentation/power/userland-swsusp.txt b/Documentation/power/userland-swsusp.txt
new file mode 100644
index 000000000000..94058220aaf0
--- /dev/null
+++ b/Documentation/power/userland-swsusp.txt
@@ -0,0 +1,149 @@
1Documentation for userland software suspend interface
2 (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
3
4First, the warnings at the beginning of swsusp.txt still apply.
5
6Second, you should read the FAQ in swsusp.txt _now_ if you have not
7done it already.
8
9Now, to use the userland interface for software suspend you need special
10utilities that will read/write the system memory snapshot from/to the
11kernel. Such utilities are available, for example, from
12<http://www.sisk.pl/kernel/utilities/suspend>. You may want to have
13a look at them if you are going to develop your own suspend/resume
14utilities.
15
16The interface consists of a character device providing the open(),
17release(), read(), and write() operations as well as several ioctl()
18commands defined in kernel/power/power.h. The major and minor
19numbers of the device are, respectively, 10 and 231, and they can
20be read from /sys/class/misc/snapshot/dev.
21
22The device can be open either for reading or for writing. If open for
23reading, it is considered to be in the suspend mode. Otherwise it is
24assumed to be in the resume mode. The device cannot be open for reading
25and writing. It is also impossible to have the device open more than once
26at a time.
27
28The ioctl() commands recognized by the device are:
29
30SNAPSHOT_FREEZE - freeze user space processes (the current process is
31 not frozen); this is required for SNAPSHOT_ATOMIC_SNAPSHOT
32 and SNAPSHOT_ATOMIC_RESTORE to succeed
33
34SNAPSHOT_UNFREEZE - thaw user space processes frozen by SNAPSHOT_FREEZE
35
36SNAPSHOT_ATOMIC_SNAPSHOT - create a snapshot of the system memory; the
37 last argument of ioctl() should be a pointer to an int variable,
38 the value of which will indicate whether the call returned after
39 creating the snapshot (1) or after restoring the system memory state
40 from it (0) (after resume the system finds itself finishing the
41 SNAPSHOT_ATOMIC_SNAPSHOT ioctl() again); after the snapshot
42 has been created the read() operation can be used to transfer
43 it out of the kernel
44
45SNAPSHOT_ATOMIC_RESTORE - restore the system memory state from the
46 uploaded snapshot image; before calling it you should transfer
47 the system memory snapshot back to the kernel using the write()
48 operation; this call will not succeed if the snapshot
49 image is not available to the kernel
50
51SNAPSHOT_FREE - free memory allocated for the snapshot image
52
53SNAPSHOT_SET_IMAGE_SIZE - set the preferred maximum size of the image
54 (the kernel will do its best to ensure the image size will not exceed
55 this number, but if it turns out to be impossible, the kernel will
56 create the smallest image possible)
57
58SNAPSHOT_AVAIL_SWAP - return the amount of available swap in bytes (the last
59 argument should be a pointer to an unsigned int variable that will
60 contain the result if the call is successful).
61
62SNAPSHOT_GET_SWAP_PAGE - allocate a swap page from the resume partition
63 (the last argument should be a pointer to a loff_t variable that
64 will contain the swap page offset if the call is successful)
65
66SNAPSHOT_FREE_SWAP_PAGES - free all swap pages allocated with
67 SNAPSHOT_GET_SWAP_PAGE
68
69SNAPSHOT_SET_SWAP_FILE - set the resume partition (the last ioctl() argument
70 should specify the device's major and minor numbers in the old
71 two-byte format, as returned by the stat() function in the .st_rdev
72 member of the stat structure); it is recommended to always use this
73 call, because the code to set the resume partition could be removed from
74 future kernels
75
76The device's read() operation can be used to transfer the snapshot image from
77the kernel. It has the following limitations:
78- you cannot read() more than one virtual memory page at a time
79- read()s accross page boundaries are impossible (ie. if ypu read() 1/2 of
80 a page in the previous call, you will only be able to read()
81 _at_ _most_ 1/2 of the page in the next call)
82
83The device's write() operation is used for uploading the system memory snapshot
84into the kernel. It has the same limitations as the read() operation.
85
86The release() operation frees all memory allocated for the snapshot image
87and all swap pages allocated with SNAPSHOT_GET_SWAP_PAGE (if any).
88Thus it is not necessary to use either SNAPSHOT_FREE or
89SNAPSHOT_FREE_SWAP_PAGES before closing the device (in fact it will also
90unfreeze user space processes frozen by SNAPSHOT_UNFREEZE if they are
91still frozen when the device is being closed).
92
93Currently it is assumed that the userland utilities reading/writing the
94snapshot image from/to the kernel will use a swap parition, called the resume
95partition, as storage space. However, this is not really required, as they
96can use, for example, a special (blank) suspend partition or a file on a partition
97that is unmounted before SNAPSHOT_ATOMIC_SNAPSHOT and mounted afterwards.
98
99These utilities SHOULD NOT make any assumptions regarding the ordering of
100data within the snapshot image, except for the image header that MAY be
101assumed to start with an swsusp_info structure, as specified in
102kernel/power/power.h. This structure MAY be used by the userland utilities
103to obtain some information about the snapshot image, such as the size
104of the snapshot image, including the metadata and the header itself,
105contained in the .size member of swsusp_info.
106
107The snapshot image MUST be written to the kernel unaltered (ie. all of the image
108data, metadata and header MUST be written in _exactly_ the same amount, form
109and order in which they have been read). Otherwise, the behavior of the
110resumed system may be totally unpredictable.
111
112While executing SNAPSHOT_ATOMIC_RESTORE the kernel checks if the
113structure of the snapshot image is consistent with the information stored
114in the image header. If any inconsistencies are detected,
115SNAPSHOT_ATOMIC_RESTORE will not succeed. Still, this is not a fool-proof
116mechanism and the userland utilities using the interface SHOULD use additional
117means, such as checksums, to ensure the integrity of the snapshot image.
118
119The suspending and resuming utilities MUST lock themselves in memory,
120preferrably using mlockall(), before calling SNAPSHOT_FREEZE.
121
122The suspending utility MUST check the value stored by SNAPSHOT_ATOMIC_SNAPSHOT
123in the memory location pointed to by the last argument of ioctl() and proceed
124in accordance with it:
1251. If the value is 1 (ie. the system memory snapshot has just been
126 created and the system is ready for saving it):
127 (a) The suspending utility MUST NOT close the snapshot device
128 _unless_ the whole suspend procedure is to be cancelled, in
129 which case, if the snapshot image has already been saved, the
130 suspending utility SHOULD destroy it, preferrably by zapping
131 its header. If the suspend is not to be cancelled, the
132 system MUST be powered off or rebooted after the snapshot
133 image has been saved.
134 (b) The suspending utility SHOULD NOT attempt to perform any
135 file system operations (including reads) on the file systems
136 that were mounted before SNAPSHOT_ATOMIC_SNAPSHOT has been
137 called. However, it MAY mount a file system that was not
138 mounted at that time and perform some operations on it (eg.
139 use it for saving the image).
1402. If the value is 0 (ie. the system state has just been restored from
141 the snapshot image), the suspending utility MUST close the snapshot
142 device. Afterwards it will be treated as a regular userland process,
143 so it need not exit.
144
145The resuming utility SHOULD NOT attempt to mount any file systems that could
146be mounted before suspend and SHOULD NOT attempt to perform any operations
147involving such file systems.
148
149For details, please refer to the source code.
diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c
index a05cabd0fd10..405f9031af87 100644
--- a/init/do_mounts_initrd.c
+++ b/init/do_mounts_initrd.c
@@ -56,6 +56,7 @@ static void __init handle_initrd(void)
56 sys_chroot("."); 56 sys_chroot(".");
57 mount_devfs_fs (); 57 mount_devfs_fs ();
58 58
59 current->flags |= PF_NOFREEZE;
59 pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); 60 pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
60 if (pid > 0) { 61 if (pid > 0) {
61 while (pid != sys_wait4(-1, NULL, 0, NULL)) 62 while (pid != sys_wait4(-1, NULL, 0, NULL))
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index bb91a0615303..8d0af3d37a4b 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -5,7 +5,7 @@ endif
5 5
6obj-y := main.o process.o console.o 6obj-y := main.o process.o console.o
7obj-$(CONFIG_PM_LEGACY) += pm.o 7obj-$(CONFIG_PM_LEGACY) += pm.o
8obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o swap.o 8obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o snapshot.o swap.o user.o
9 9
10obj-$(CONFIG_SUSPEND_SMP) += smp.o 10obj-$(CONFIG_SUSPEND_SMP) += smp.o
11 11
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 5d1abffbb9ce..42c431c8bdde 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -8,6 +8,7 @@ struct swsusp_info {
8 int cpus; 8 int cpus;
9 unsigned long image_pages; 9 unsigned long image_pages;
10 unsigned long pages; 10 unsigned long pages;
11 unsigned long size;
11} __attribute__((aligned(PAGE_SIZE))); 12} __attribute__((aligned(PAGE_SIZE)));
12 13
13 14
@@ -65,6 +66,19 @@ extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
65extern int snapshot_write_next(struct snapshot_handle *handle, size_t count); 66extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
66int snapshot_image_loaded(struct snapshot_handle *handle); 67int snapshot_image_loaded(struct snapshot_handle *handle);
67 68
69#define SNAPSHOT_IOC_MAGIC '3'
70#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
71#define SNAPSHOT_UNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
72#define SNAPSHOT_ATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
73#define SNAPSHOT_ATOMIC_RESTORE _IO(SNAPSHOT_IOC_MAGIC, 4)
74#define SNAPSHOT_FREE _IO(SNAPSHOT_IOC_MAGIC, 5)
75#define SNAPSHOT_SET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
76#define SNAPSHOT_AVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
77#define SNAPSHOT_GET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
78#define SNAPSHOT_FREE_SWAP_PAGES _IO(SNAPSHOT_IOC_MAGIC, 9)
79#define SNAPSHOT_SET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
80#define SNAPSHOT_IOC_MAXNR 10
81
68/** 82/**
69 * The bitmap is used for tracing allocated swap pages 83 * The bitmap is used for tracing allocated swap pages
70 * 84 *
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index cc349437fb72..0036955357e0 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -37,6 +37,7 @@
37struct pbe *pagedir_nosave; 37struct pbe *pagedir_nosave;
38static unsigned int nr_copy_pages; 38static unsigned int nr_copy_pages;
39static unsigned int nr_meta_pages; 39static unsigned int nr_meta_pages;
40static unsigned long *buffer;
40 41
41#ifdef CONFIG_HIGHMEM 42#ifdef CONFIG_HIGHMEM
42unsigned int count_highmem_pages(void) 43unsigned int count_highmem_pages(void)
@@ -389,7 +390,7 @@ struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed
389 free_pagedir(pblist); 390 free_pagedir(pblist);
390 pblist = NULL; 391 pblist = NULL;
391 } else 392 } else
392 create_pbe_list(pblist, nr_pages); 393 create_pbe_list(pblist, nr_pages);
393 return pblist; 394 return pblist;
394} 395}
395 396
@@ -418,6 +419,7 @@ void swsusp_free(void)
418 nr_copy_pages = 0; 419 nr_copy_pages = 0;
419 nr_meta_pages = 0; 420 nr_meta_pages = 0;
420 pagedir_nosave = NULL; 421 pagedir_nosave = NULL;
422 buffer = NULL;
421} 423}
422 424
423 425
@@ -523,6 +525,8 @@ static void init_header(struct swsusp_info *info)
523 info->cpus = num_online_cpus(); 525 info->cpus = num_online_cpus();
524 info->image_pages = nr_copy_pages; 526 info->image_pages = nr_copy_pages;
525 info->pages = nr_copy_pages + nr_meta_pages + 1; 527 info->pages = nr_copy_pages + nr_meta_pages + 1;
528 info->size = info->pages;
529 info->size <<= PAGE_SHIFT;
526} 530}
527 531
528/** 532/**
@@ -568,8 +572,6 @@ static inline struct pbe *pack_orig_addresses(unsigned long *buf, struct pbe *pb
568 572
569int snapshot_read_next(struct snapshot_handle *handle, size_t count) 573int snapshot_read_next(struct snapshot_handle *handle, size_t count)
570{ 574{
571 static unsigned long *buffer;
572
573 if (handle->page > nr_meta_pages + nr_copy_pages) 575 if (handle->page > nr_meta_pages + nr_copy_pages)
574 return 0; 576 return 0;
575 if (!buffer) { 577 if (!buffer) {
@@ -774,7 +776,6 @@ static int create_image(struct snapshot_handle *handle)
774 776
775int snapshot_write_next(struct snapshot_handle *handle, size_t count) 777int snapshot_write_next(struct snapshot_handle *handle, size_t count)
776{ 778{
777 static unsigned long *buffer;
778 int error = 0; 779 int error = 0;
779 780
780 if (handle->prev && handle->page > nr_meta_pages + nr_copy_pages) 781 if (handle->prev && handle->page > nr_meta_pages + nr_copy_pages)
diff --git a/kernel/power/user.c b/kernel/power/user.c
new file mode 100644
index 000000000000..8cabc405ca10
--- /dev/null
+++ b/kernel/power/user.c
@@ -0,0 +1,301 @@
1/*
2 * linux/kernel/power/user.c
3 *
4 * This file provides the user space interface for software suspend/resume.
5 *
6 * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
7 *
8 * This file is released under the GPLv2.
9 *
10 */
11
12#include <linux/suspend.h>
13#include <linux/syscalls.h>
14#include <linux/string.h>
15#include <linux/device.h>
16#include <linux/miscdevice.h>
17#include <linux/mm.h>
18#include <linux/swap.h>
19#include <linux/swapops.h>
20#include <linux/pm.h>
21#include <linux/fs.h>
22
23#include <asm/uaccess.h>
24
25#include "power.h"
26
27#define SNAPSHOT_MINOR 231
28
29static struct snapshot_data {
30 struct snapshot_handle handle;
31 int swap;
32 struct bitmap_page *bitmap;
33 int mode;
34 char frozen;
35 char ready;
36} snapshot_state;
37
38static atomic_t device_available = ATOMIC_INIT(1);
39
40static int snapshot_open(struct inode *inode, struct file *filp)
41{
42 struct snapshot_data *data;
43
44 if (!atomic_add_unless(&device_available, -1, 0))
45 return -EBUSY;
46
47 if ((filp->f_flags & O_ACCMODE) == O_RDWR)
48 return -ENOSYS;
49
50 nonseekable_open(inode, filp);
51 data = &snapshot_state;
52 filp->private_data = data;
53 memset(&data->handle, 0, sizeof(struct snapshot_handle));
54 if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
55 data->swap = swsusp_resume_device ? swap_type_of(swsusp_resume_device) : -1;
56 data->mode = O_RDONLY;
57 } else {
58 data->swap = -1;
59 data->mode = O_WRONLY;
60 }
61 data->bitmap = NULL;
62 data->frozen = 0;
63 data->ready = 0;
64
65 return 0;
66}
67
68static int snapshot_release(struct inode *inode, struct file *filp)
69{
70 struct snapshot_data *data;
71
72 swsusp_free();
73 data = filp->private_data;
74 free_all_swap_pages(data->swap, data->bitmap);
75 free_bitmap(data->bitmap);
76 if (data->frozen) {
77 down(&pm_sem);
78 thaw_processes();
79 enable_nonboot_cpus();
80 up(&pm_sem);
81 }
82 atomic_inc(&device_available);
83 return 0;
84}
85
86static ssize_t snapshot_read(struct file *filp, char __user *buf,
87 size_t count, loff_t *offp)
88{
89 struct snapshot_data *data;
90 ssize_t res;
91
92 data = filp->private_data;
93 res = snapshot_read_next(&data->handle, count);
94 if (res > 0) {
95 if (copy_to_user(buf, data_of(data->handle), res))
96 res = -EFAULT;
97 else
98 *offp = data->handle.offset;
99 }
100 return res;
101}
102
103static ssize_t snapshot_write(struct file *filp, const char __user *buf,
104 size_t count, loff_t *offp)
105{
106 struct snapshot_data *data;
107 ssize_t res;
108
109 data = filp->private_data;
110 res = snapshot_write_next(&data->handle, count);
111 if (res > 0) {
112 if (copy_from_user(data_of(data->handle), buf, res))
113 res = -EFAULT;
114 else
115 *offp = data->handle.offset;
116 }
117 return res;
118}
119
120static int snapshot_ioctl(struct inode *inode, struct file *filp,
121 unsigned int cmd, unsigned long arg)
122{
123 int error = 0;
124 struct snapshot_data *data;
125 loff_t offset, avail;
126
127 if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
128 return -ENOTTY;
129 if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
130 return -ENOTTY;
131 if (!capable(CAP_SYS_ADMIN))
132 return -EPERM;
133
134 data = filp->private_data;
135
136 switch (cmd) {
137
138 case SNAPSHOT_FREEZE:
139 if (data->frozen)
140 break;
141 sys_sync();
142 down(&pm_sem);
143 pm_prepare_console();
144 disable_nonboot_cpus();
145 if (freeze_processes()) {
146 thaw_processes();
147 enable_nonboot_cpus();
148 pm_restore_console();
149 error = -EBUSY;
150 }
151 up(&pm_sem);
152 if (!error)
153 data->frozen = 1;
154 break;
155
156 case SNAPSHOT_UNFREEZE:
157 if (!data->frozen)
158 break;
159 down(&pm_sem);
160 thaw_processes();
161 enable_nonboot_cpus();
162 pm_restore_console();
163 up(&pm_sem);
164 data->frozen = 0;
165 break;
166
167 case SNAPSHOT_ATOMIC_SNAPSHOT:
168 if (data->mode != O_RDONLY || !data->frozen || data->ready) {
169 error = -EPERM;
170 break;
171 }
172 down(&pm_sem);
173 /* Free memory before shutting down devices. */
174 error = swsusp_shrink_memory();
175 if (!error) {
176 error = device_suspend(PMSG_FREEZE);
177 if (!error) {
178 in_suspend = 1;
179 error = swsusp_suspend();
180 device_resume();
181 }
182 }
183 up(&pm_sem);
184 if (!error)
185 error = put_user(in_suspend, (unsigned int __user *)arg);
186 if (!error)
187 data->ready = 1;
188 break;
189
190 case SNAPSHOT_ATOMIC_RESTORE:
191 if (data->mode != O_WRONLY || !data->frozen ||
192 !snapshot_image_loaded(&data->handle)) {
193 error = -EPERM;
194 break;
195 }
196 down(&pm_sem);
197 pm_prepare_console();
198 error = device_suspend(PMSG_FREEZE);
199 if (!error) {
200 error = swsusp_resume();
201 device_resume();
202 }
203 pm_restore_console();
204 up(&pm_sem);
205 break;
206
207 case SNAPSHOT_FREE:
208 swsusp_free();
209 memset(&data->handle, 0, sizeof(struct snapshot_handle));
210 data->ready = 0;
211 break;
212
213 case SNAPSHOT_SET_IMAGE_SIZE:
214 image_size = arg;
215 break;
216
217 case SNAPSHOT_AVAIL_SWAP:
218 avail = count_swap_pages(data->swap, 1);
219 avail <<= PAGE_SHIFT;
220 error = put_user(avail, (loff_t __user *)arg);
221 break;
222
223 case SNAPSHOT_GET_SWAP_PAGE:
224 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
225 error = -ENODEV;
226 break;
227 }
228 if (!data->bitmap) {
229 data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0));
230 if (!data->bitmap) {
231 error = -ENOMEM;
232 break;
233 }
234 }
235 offset = alloc_swap_page(data->swap, data->bitmap);
236 if (offset) {
237 offset <<= PAGE_SHIFT;
238 error = put_user(offset, (loff_t __user *)arg);
239 } else {
240 error = -ENOSPC;
241 }
242 break;
243
244 case SNAPSHOT_FREE_SWAP_PAGES:
245 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
246 error = -ENODEV;
247 break;
248 }
249 free_all_swap_pages(data->swap, data->bitmap);
250 free_bitmap(data->bitmap);
251 data->bitmap = NULL;
252 break;
253
254 case SNAPSHOT_SET_SWAP_FILE:
255 if (!data->bitmap) {
256 /*
257 * User space encodes device types as two-byte values,
258 * so we need to recode them
259 */
260 if (old_decode_dev(arg)) {
261 data->swap = swap_type_of(old_decode_dev(arg));
262 if (data->swap < 0)
263 error = -ENODEV;
264 } else {
265 data->swap = -1;
266 error = -EINVAL;
267 }
268 } else {
269 error = -EPERM;
270 }
271 break;
272
273 default:
274 error = -ENOTTY;
275
276 }
277
278 return error;
279}
280
281static struct file_operations snapshot_fops = {
282 .open = snapshot_open,
283 .release = snapshot_release,
284 .read = snapshot_read,
285 .write = snapshot_write,
286 .llseek = no_llseek,
287 .ioctl = snapshot_ioctl,
288};
289
290static struct miscdevice snapshot_device = {
291 .minor = SNAPSHOT_MINOR,
292 .name = "snapshot",
293 .fops = &snapshot_fops,
294};
295
296static int __init snapshot_device_init(void)
297{
298 return misc_register(&snapshot_device);
299};
300
301device_initcall(snapshot_device_init);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 4d11f9d84666..39aa9d129612 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -428,14 +428,16 @@ int swap_type_of(dev_t device)
428{ 428{
429 int i; 429 int i;
430 430
431 if (!device)
432 return -EINVAL;
433 spin_lock(&swap_lock); 431 spin_lock(&swap_lock);
434 for (i = 0; i < nr_swapfiles; i++) { 432 for (i = 0; i < nr_swapfiles; i++) {
435 struct inode *inode; 433 struct inode *inode;
436 434
437 if (!(swap_info[i].flags & SWP_WRITEOK)) 435 if (!(swap_info[i].flags & SWP_WRITEOK))
438 continue; 436 continue;
437 if (!device) {
438 spin_unlock(&swap_lock);
439 return i;
440 }
439 inode = swap_info->swap_file->f_dentry->d_inode; 441 inode = swap_info->swap_file->f_dentry->d_inode;
440 if (S_ISBLK(inode->i_mode) && 442 if (S_ISBLK(inode->i_mode) &&
441 device == MKDEV(imajor(inode), iminor(inode))) { 443 device == MKDEV(imajor(inode), iminor(inode))) {