blob: b5815685b944fe770d7d7e14b57c46a611b335c9 [file] [log] [blame]
Thomas Gleixner55716d22019-06-01 10:08:42 +02001// SPDX-License-Identifier: GPL-2.0-only
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -08002/*
3 * linux/kernel/power/user.c
4 *
5 * This file provides the user space interface for software suspend/resume.
6 *
7 * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -08008 */
9
10#include <linux/suspend.h>
Stefan Seyfried35926952006-12-06 20:34:06 -080011#include <linux/reboot.h>
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080012#include <linux/string.h>
13#include <linux/device.h>
14#include <linux/miscdevice.h>
15#include <linux/mm.h>
16#include <linux/swap.h>
17#include <linux/swapops.h>
18#include <linux/pm.h>
19#include <linux/fs.h>
Ben Hutchingsc3360782011-12-27 22:54:52 +010020#include <linux/compat.h>
Rafael J. Wysocki97c78012006-10-11 01:20:45 -070021#include <linux/console.h>
Rafael J. Wysockie3920fb2006-09-25 23:32:48 -070022#include <linux/cpu.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080023#include <linux/freezer.h>
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080024
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080025#include <linux/uaccess.h>
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080026
27#include "power.h"
28
Rafael J. Wysockieb57c1c2007-10-26 01:01:10 +020029
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080030static struct snapshot_data {
31 struct snapshot_handle handle;
32 int swap;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080033 int mode;
Rafael J. Wysocki7bc9b1c2013-10-18 22:20:40 +020034 bool frozen;
35 bool ready;
36 bool platform_support;
Rafael J. Wysockiaab17282013-09-30 19:40:56 +020037 bool free_bitmaps;
Christoph Hellwigbb3247a392020-09-21 09:19:55 +020038 dev_t dev;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080039} snapshot_state;
40
Christoph Hellwigbb3247a392020-09-21 09:19:55 +020041int is_hibernate_resume_dev(dev_t dev)
Domenico Andreoliad1e4f72020-05-19 20:14:10 +020042{
Christoph Hellwigbb3247a392020-09-21 09:19:55 +020043 return hibernation_available() && snapshot_state.dev == dev;
Domenico Andreoliad1e4f72020-05-19 20:14:10 +020044}
45
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080046static int snapshot_open(struct inode *inode, struct file *filp)
47{
48 struct snapshot_data *data;
Lianwei Wangea00f4f2016-06-19 23:52:27 -070049 int error, nr_calls = 0;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080050
Kees Cooka6e15a32014-06-13 13:30:35 -070051 if (!hibernation_available())
52 return -EPERM;
53
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +010054 lock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +020055
Domenico Andreoliab7e9b02020-05-07 09:19:52 +020056 if (!hibernate_acquire()) {
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +020057 error = -EBUSY;
58 goto Unlock;
59 }
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080060
Rafael J. Wysocki1525a2a2007-05-06 14:50:44 -070061 if ((filp->f_flags & O_ACCMODE) == O_RDWR) {
Domenico Andreoliab7e9b02020-05-07 09:19:52 +020062 hibernate_release();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +020063 error = -ENOSYS;
64 goto Unlock;
Rafael J. Wysocki1525a2a2007-05-06 14:50:44 -070065 }
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080066 nonseekable_open(inode, filp);
67 data = &snapshot_state;
68 filp->private_data = data;
69 memset(&data->handle, 0, sizeof(struct snapshot_handle));
70 if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
Rafael J. Wysockic7510852009-04-12 20:06:56 +020071 /* Hibernating. The image device should be accessible. */
Rafael J. Wysocki915bae92006-12-06 20:34:07 -080072 data->swap = swsusp_resume_device ?
Rafael J. Wysocki7bf23682007-01-05 16:36:28 -080073 swap_type_of(swsusp_resume_device, 0, NULL) : -1;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -080074 data->mode = O_RDONLY;
Rafael J. Wysocki6a0c7cd2013-11-14 23:26:58 +010075 data->free_bitmaps = false;
Lianwei Wangea00f4f2016-06-19 23:52:27 -070076 error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
Alan Sternc3e94d82007-11-19 23:38:25 +010077 if (error)
Lianwei Wangea00f4f2016-06-19 23:52:27 -070078 __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL);
Andrey Borzenkovebae2602009-02-14 02:05:14 +010079 } else {
Rafael J. Wysockic7510852009-04-12 20:06:56 +020080 /*
81 * Resuming. We may need to wait for the image device to
82 * appear.
83 */
84 wait_for_device_probe();
Rafael J. Wysockic7510852009-04-12 20:06:56 +020085
Andrey Borzenkovebae2602009-02-14 02:05:14 +010086 data->swap = -1;
87 data->mode = O_WRONLY;
Lianwei Wangea00f4f2016-06-19 23:52:27 -070088 error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls);
Rafael J. Wysockiaab17282013-09-30 19:40:56 +020089 if (!error) {
90 error = create_basic_memory_bitmaps();
91 data->free_bitmaps = !error;
Lianwei Wangea00f4f2016-06-19 23:52:27 -070092 } else
93 nr_calls--;
94
Andrey Borzenkovebae2602009-02-14 02:05:14 +010095 if (error)
Lianwei Wangea00f4f2016-06-19 23:52:27 -070096 __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
Alan Sternc3e94d82007-11-19 23:38:25 +010097 }
Rafael J. Wysocki8fd37a42013-08-30 14:19:38 +020098 if (error)
Domenico Andreoliab7e9b02020-05-07 09:19:52 +020099 hibernate_release();
Rafael J. Wysocki8fd37a42013-08-30 14:19:38 +0200100
Rafael J. Wysocki7bc9b1c2013-10-18 22:20:40 +0200101 data->frozen = false;
102 data->ready = false;
103 data->platform_support = false;
Christoph Hellwigbb3247a392020-09-21 09:19:55 +0200104 data->dev = 0;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800105
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200106 Unlock:
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100107 unlock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200108
109 return error;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800110}
111
112static int snapshot_release(struct inode *inode, struct file *filp)
113{
114 struct snapshot_data *data;
115
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100116 lock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200117
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800118 swsusp_free();
119 data = filp->private_data;
Christoph Hellwigbb3247a392020-09-21 09:19:55 +0200120 data->dev = 0;
Rafael J. Wysockid1d241c2007-05-06 14:50:47 -0700121 free_all_swap_pages(data->swap);
Rafael J. Wysocki97449972011-05-10 21:10:01 +0200122 if (data->frozen) {
123 pm_restore_gfp_mask();
Rafael J. Wysocki8fd37a42013-08-30 14:19:38 +0200124 free_basic_memory_bitmaps();
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800125 thaw_processes();
Rafael J. Wysockiaab17282013-09-30 19:40:56 +0200126 } else if (data->free_bitmaps) {
127 free_basic_memory_bitmaps();
Rafael J. Wysocki97449972011-05-10 21:10:01 +0200128 }
Takashi Iwai1497dd12010-12-10 00:16:39 +0100129 pm_notifier_call_chain(data->mode == O_RDONLY ?
Alan Sternc3e94d82007-11-19 23:38:25 +0100130 PM_POST_HIBERNATION : PM_POST_RESTORE);
Domenico Andreoliab7e9b02020-05-07 09:19:52 +0200131 hibernate_release();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200132
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100133 unlock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200134
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800135 return 0;
136}
137
138static ssize_t snapshot_read(struct file *filp, char __user *buf,
139 size_t count, loff_t *offp)
140{
141 struct snapshot_data *data;
142 ssize_t res;
Jiri Slabyd3c1b242010-05-01 23:52:02 +0200143 loff_t pg_offp = *offp & ~PAGE_MASK;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800144
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100145 lock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200146
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800147 data = filp->private_data;
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200148 if (!data->ready) {
149 res = -ENODATA;
150 goto Unlock;
151 }
Jiri Slabyd3c1b242010-05-01 23:52:02 +0200152 if (!pg_offp) { /* on page boundary? */
153 res = snapshot_read_next(&data->handle);
154 if (res <= 0)
155 goto Unlock;
156 } else {
157 res = PAGE_SIZE - pg_offp;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800158 }
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200159
Jiri Slabyd3c1b242010-05-01 23:52:02 +0200160 res = simple_read_from_buffer(buf, count, &pg_offp,
161 data_of(data->handle), res);
162 if (res > 0)
163 *offp += res;
164
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200165 Unlock:
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100166 unlock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200167
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800168 return res;
169}
170
171static ssize_t snapshot_write(struct file *filp, const char __user *buf,
172 size_t count, loff_t *offp)
173{
174 struct snapshot_data *data;
175 ssize_t res;
Jiri Slabyd3c1b242010-05-01 23:52:02 +0200176 loff_t pg_offp = *offp & ~PAGE_MASK;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800177
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100178 lock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200179
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800180 data = filp->private_data;
Jiri Slabyd3c1b242010-05-01 23:52:02 +0200181
182 if (!pg_offp) {
183 res = snapshot_write_next(&data->handle);
184 if (res <= 0)
185 goto unlock;
186 } else {
187 res = PAGE_SIZE - pg_offp;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800188 }
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200189
Tetsuo Handafc14eeb2018-05-26 09:59:36 +0900190 if (!data_of(data->handle)) {
191 res = -EINVAL;
192 goto unlock;
193 }
194
Jiri Slabyd3c1b242010-05-01 23:52:02 +0200195 res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp,
196 buf, count);
197 if (res > 0)
198 *offp += res;
199unlock:
Srivatsa S. Bhatbcda53f2011-12-07 22:29:54 +0100200 unlock_system_sleep();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200201
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800202 return res;
203}
204
Christoph Hellwig0f5c4c62020-04-06 13:58:35 +0200205struct compat_resume_swap_area {
206 compat_loff_t offset;
207 u32 dev;
208} __packed;
209
Christoph Hellwig88a77552020-04-06 13:58:34 +0200210static int snapshot_set_swap_area(struct snapshot_data *data,
211 void __user *argp)
212{
Domenico Andreoliad1e4f72020-05-19 20:14:10 +0200213 struct block_device *bdev;
Christoph Hellwig88a77552020-04-06 13:58:34 +0200214 sector_t offset;
215 dev_t swdev;
216
217 if (swsusp_swap_in_use())
218 return -EPERM;
Christoph Hellwig0f5c4c62020-04-06 13:58:35 +0200219
220 if (in_compat_syscall()) {
221 struct compat_resume_swap_area swap_area;
222
223 if (copy_from_user(&swap_area, argp, sizeof(swap_area)))
224 return -EFAULT;
225 swdev = new_decode_dev(swap_area.dev);
226 offset = swap_area.offset;
227 } else {
228 struct resume_swap_area swap_area;
229
230 if (copy_from_user(&swap_area, argp, sizeof(swap_area)))
231 return -EFAULT;
232 swdev = new_decode_dev(swap_area.dev);
233 offset = swap_area.offset;
234 }
Christoph Hellwig88a77552020-04-06 13:58:34 +0200235
236 /*
237 * User space encodes device types as two-byte values,
238 * so we need to recode them
239 */
Christoph Hellwig88a77552020-04-06 13:58:34 +0200240 if (!swdev) {
241 data->swap = -1;
242 return -EINVAL;
243 }
Domenico Andreoliad1e4f72020-05-19 20:14:10 +0200244 data->swap = swap_type_of(swdev, offset, &bdev);
Christoph Hellwig88a77552020-04-06 13:58:34 +0200245 if (data->swap < 0)
246 return -ENODEV;
Domenico Andreoliad1e4f72020-05-19 20:14:10 +0200247
Christoph Hellwigbb3247a392020-09-21 09:19:55 +0200248 data->dev = bdev->bd_dev;
Domenico Andreoliad1e4f72020-05-19 20:14:10 +0200249 bdput(bdev);
Christoph Hellwig88a77552020-04-06 13:58:34 +0200250 return 0;
251}
252
Alan Cox52d11022008-06-11 22:07:52 +0200253static long snapshot_ioctl(struct file *filp, unsigned int cmd,
254 unsigned long arg)
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800255{
256 int error = 0;
257 struct snapshot_data *data;
Rafael J. Wysockiaf508b32007-10-26 00:59:31 +0200258 loff_t size;
Rafael J. Wysocki3aef83e2006-12-06 20:34:10 -0800259 sector_t offset;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800260
261 if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
262 return -ENOTTY;
263 if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
264 return -ENOTTY;
265 if (!capable(CAP_SYS_ADMIN))
266 return -EPERM;
267
Pingfan Liu55f25032018-07-31 16:51:32 +0800268 if (!mutex_trylock(&system_transition_mutex))
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200269 return -EBUSY;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800270
Rafael J. Wysocki942f4012013-08-30 14:19:46 +0200271 lock_device_hotplug();
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200272 data = filp->private_data;
Alan Cox52d11022008-06-11 22:07:52 +0200273
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800274 switch (cmd) {
275
276 case SNAPSHOT_FREEZE:
277 if (data->frozen)
278 break;
Rafael J. Wysocki1bfcf132008-10-15 22:01:21 -0700279
Harry Panb5dee312019-02-25 20:36:41 +0800280 ksys_sync_helper();
Rafael J. Wysocki232b1432007-10-18 03:04:44 -0700281
Rafael J. Wysocki1bfcf132008-10-15 22:01:21 -0700282 error = freeze_processes();
Rafael J. Wysocki8fd37a42013-08-30 14:19:38 +0200283 if (error)
284 break;
285
286 error = create_basic_memory_bitmaps();
287 if (error)
288 thaw_processes();
289 else
Rafael J. Wysocki7bc9b1c2013-10-18 22:20:40 +0200290 data->frozen = true;
Rafael J. Wysocki8fd37a42013-08-30 14:19:38 +0200291
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800292 break;
293
294 case SNAPSHOT_UNFREEZE:
Rafael J. Wysocki2f41ddd2007-06-16 10:16:03 -0700295 if (!data->frozen || data->ready)
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800296 break;
Rafael J. Wysockic9e664f2010-12-03 22:57:45 +0100297 pm_restore_gfp_mask();
Rafael J. Wysocki8fd37a42013-08-30 14:19:38 +0200298 free_basic_memory_bitmaps();
Rafael J. Wysockiaab17282013-09-30 19:40:56 +0200299 data->free_bitmaps = false;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800300 thaw_processes();
Rafael J. Wysocki7bc9b1c2013-10-18 22:20:40 +0200301 data->frozen = false;
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800302 break;
303
Jiri Slabyb694e522010-01-27 23:47:50 +0100304 case SNAPSHOT_CREATE_IMAGE:
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800305 if (data->mode != O_RDONLY || !data->frozen || data->ready) {
306 error = -EPERM;
307 break;
308 }
Rafael J. Wysockic9e664f2010-12-03 22:57:45 +0100309 pm_restore_gfp_mask();
Rafael J. Wysockieb57c1c2007-10-26 01:01:10 +0200310 error = hibernation_snapshot(data->platform_support);
Srivatsa S. Bhat51d6ff72012-02-04 22:26:38 +0100311 if (!error) {
Rafael J. Wysockicc5d2072007-10-26 01:03:33 +0200312 error = put_user(in_suspend, (int __user *)arg);
Srivatsa S. Bhata556d5b52012-02-04 23:39:56 +0100313 data->ready = !freezer_test_done && !error;
314 freezer_test_done = false;
Srivatsa S. Bhat97819a22011-12-01 22:33:10 +0100315 }
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800316 break;
317
318 case SNAPSHOT_ATOMIC_RESTORE:
Rafael J. Wysocki83573762006-12-06 20:34:18 -0800319 snapshot_write_finalize(&data->handle);
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800320 if (data->mode != O_WRONLY || !data->frozen ||
321 !snapshot_image_loaded(&data->handle)) {
322 error = -EPERM;
323 break;
324 }
Rafael J. Wysockieb57c1c2007-10-26 01:01:10 +0200325 error = hibernation_restore(data->platform_support);
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800326 break;
327
328 case SNAPSHOT_FREE:
329 swsusp_free();
330 memset(&data->handle, 0, sizeof(struct snapshot_handle));
Rafael J. Wysocki7bc9b1c2013-10-18 22:20:40 +0200331 data->ready = false;
Rafael J. Wysocki181e9bd2012-01-29 20:35:52 +0100332 /*
333 * It is necessary to thaw kernel threads here, because
334 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
335 * SNAPSHOT_FREE. In that case, if kernel threads were not
336 * thawed, the preallocation of memory carried out by
337 * hibernation_snapshot() might run into problems (i.e. it
338 * might fail or even deadlock).
339 */
340 thaw_kernel_threads();
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800341 break;
342
Jiri Slabyb694e522010-01-27 23:47:50 +0100343 case SNAPSHOT_PREF_IMAGE_SIZE:
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800344 image_size = arg;
345 break;
346
Rafael J. Wysockiaf508b32007-10-26 00:59:31 +0200347 case SNAPSHOT_GET_IMAGE_SIZE:
348 if (!data->ready) {
349 error = -ENODATA;
350 break;
351 }
352 size = snapshot_get_image_size();
353 size <<= PAGE_SHIFT;
354 error = put_user(size, (loff_t __user *)arg);
355 break;
356
Jiri Slabyb694e522010-01-27 23:47:50 +0100357 case SNAPSHOT_AVAIL_SWAP_SIZE:
Rafael J. Wysockiaf508b32007-10-26 00:59:31 +0200358 size = count_swap_pages(data->swap, 1);
359 size <<= PAGE_SHIFT;
360 error = put_user(size, (loff_t __user *)arg);
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800361 break;
362
Jiri Slabyb694e522010-01-27 23:47:50 +0100363 case SNAPSHOT_ALLOC_SWAP_PAGE:
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800364 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
365 error = -ENODEV;
366 break;
367 }
Rafael J. Wysockid1d241c2007-05-06 14:50:47 -0700368 offset = alloc_swapdev_block(data->swap);
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800369 if (offset) {
370 offset <<= PAGE_SHIFT;
Rafael J. Wysockicc5d2072007-10-26 01:03:33 +0200371 error = put_user(offset, (loff_t __user *)arg);
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800372 } else {
373 error = -ENOSPC;
374 }
375 break;
376
377 case SNAPSHOT_FREE_SWAP_PAGES:
378 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
379 error = -ENODEV;
380 break;
381 }
Rafael J. Wysockid1d241c2007-05-06 14:50:47 -0700382 free_all_swap_pages(data->swap);
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800383 break;
384
Luca Tettamanti9b238202006-03-23 03:00:09 -0800385 case SNAPSHOT_S2RAM:
386 if (!data->frozen) {
387 error = -EPERM;
388 break;
389 }
Rafael J. Wysocki6c961df2007-07-19 01:47:38 -0700390 /*
391 * Tasks are frozen and the notifiers have been called with
392 * PM_HIBERNATION_PREPARE
393 */
394 error = suspend_devices_and_enter(PM_SUSPEND_MEM);
Rafael J. Wysocki7bc9b1c2013-10-18 22:20:40 +0200395 data->ready = false;
Luca Tettamanti9b238202006-03-23 03:00:09 -0800396 break;
397
Rafael J. Wysockieb57c1c2007-10-26 01:01:10 +0200398 case SNAPSHOT_PLATFORM_SUPPORT:
399 data->platform_support = !!arg;
400 break;
401
402 case SNAPSHOT_POWER_OFF:
403 if (data->platform_support)
404 error = hibernation_platform_enter();
405 break;
406
Rafael J. Wysocki37b2ba12006-12-06 20:34:15 -0800407 case SNAPSHOT_SET_SWAP_AREA:
Christoph Hellwig88a77552020-04-06 13:58:34 +0200408 error = snapshot_set_swap_area(data, (void __user *)arg);
Rafael J. Wysocki37b2ba12006-12-06 20:34:15 -0800409 break;
410
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800411 default:
412 error = -ENOTTY;
413
414 }
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200415
Rafael J. Wysocki942f4012013-08-30 14:19:46 +0200416 unlock_device_hotplug();
Pingfan Liu55f25032018-07-31 16:51:32 +0800417 mutex_unlock(&system_transition_mutex);
Rafael J. Wysocki25f2f3d2008-06-11 22:09:45 +0200418
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800419 return error;
420}
421
Ben Hutchingsc3360782011-12-27 22:54:52 +0100422#ifdef CONFIG_COMPAT
Ben Hutchingsc3360782011-12-27 22:54:52 +0100423static long
424snapshot_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
425{
426 BUILD_BUG_ON(sizeof(loff_t) != sizeof(compat_loff_t));
427
428 switch (cmd) {
429 case SNAPSHOT_GET_IMAGE_SIZE:
430 case SNAPSHOT_AVAIL_SWAP_SIZE:
Eric Biggersfba616a2020-03-07 19:27:01 -0800431 case SNAPSHOT_ALLOC_SWAP_PAGE:
Ben Hutchingsc3360782011-12-27 22:54:52 +0100432 case SNAPSHOT_CREATE_IMAGE:
Christoph Hellwig0f5c4c62020-04-06 13:58:35 +0200433 case SNAPSHOT_SET_SWAP_AREA:
Ben Hutchingsc3360782011-12-27 22:54:52 +0100434 return snapshot_ioctl(file, cmd,
435 (unsigned long) compat_ptr(arg));
Ben Hutchingsc3360782011-12-27 22:54:52 +0100436 default:
437 return snapshot_ioctl(file, cmd, arg);
438 }
439}
Ben Hutchingsc3360782011-12-27 22:54:52 +0100440#endif /* CONFIG_COMPAT */
441
Helge Deller15ad7cd2006-12-06 20:40:36 -0800442static const struct file_operations snapshot_fops = {
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800443 .open = snapshot_open,
444 .release = snapshot_release,
445 .read = snapshot_read,
446 .write = snapshot_write,
447 .llseek = no_llseek,
Alan Cox52d11022008-06-11 22:07:52 +0200448 .unlocked_ioctl = snapshot_ioctl,
Ben Hutchingsc3360782011-12-27 22:54:52 +0100449#ifdef CONFIG_COMPAT
450 .compat_ioctl = snapshot_compat_ioctl,
451#endif
Rafael J. Wysocki6e1819d2006-03-23 03:00:03 -0800452};
453
454static struct miscdevice snapshot_device = {
455 .minor = SNAPSHOT_MINOR,
456 .name = "snapshot",
457 .fops = &snapshot_fops,
458};
459
460static int __init snapshot_device_init(void)
461{
462 return misc_register(&snapshot_device);
463};
464
465device_initcall(snapshot_device_init);