xref: /openbmc/linux/kernel/power/user.c (revision 915bae9ebe41e52d71ad8b06d50e4ab26189f964)
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/reboot.h>
15 #include <linux/string.h>
16 #include <linux/device.h>
17 #include <linux/miscdevice.h>
18 #include <linux/mm.h>
19 #include <linux/swap.h>
20 #include <linux/swapops.h>
21 #include <linux/pm.h>
22 #include <linux/fs.h>
23 #include <linux/console.h>
24 #include <linux/cpu.h>
25 
26 #include <asm/uaccess.h>
27 
28 #include "power.h"
29 
30 #define SNAPSHOT_MINOR	231
31 
32 static struct snapshot_data {
33 	struct snapshot_handle handle;
34 	int swap;
35 	struct bitmap_page *bitmap;
36 	int mode;
37 	char frozen;
38 	char ready;
39 } snapshot_state;
40 
41 static atomic_t device_available = ATOMIC_INIT(1);
42 
43 static int snapshot_open(struct inode *inode, struct file *filp)
44 {
45 	struct snapshot_data *data;
46 
47 	if (!atomic_add_unless(&device_available, -1, 0))
48 		return -EBUSY;
49 
50 	if ((filp->f_flags & O_ACCMODE) == O_RDWR)
51 		return -ENOSYS;
52 
53 	nonseekable_open(inode, filp);
54 	data = &snapshot_state;
55 	filp->private_data = data;
56 	memset(&data->handle, 0, sizeof(struct snapshot_handle));
57 	if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
58 		data->swap = swsusp_resume_device ?
59 				swap_type_of(swsusp_resume_device, 0) : -1;
60 		data->mode = O_RDONLY;
61 	} else {
62 		data->swap = -1;
63 		data->mode = O_WRONLY;
64 	}
65 	data->bitmap = NULL;
66 	data->frozen = 0;
67 	data->ready = 0;
68 
69 	return 0;
70 }
71 
72 static int snapshot_release(struct inode *inode, struct file *filp)
73 {
74 	struct snapshot_data *data;
75 
76 	swsusp_free();
77 	data = filp->private_data;
78 	free_all_swap_pages(data->swap, data->bitmap);
79 	free_bitmap(data->bitmap);
80 	if (data->frozen) {
81 		down(&pm_sem);
82 		thaw_processes();
83 		enable_nonboot_cpus();
84 		up(&pm_sem);
85 	}
86 	atomic_inc(&device_available);
87 	return 0;
88 }
89 
90 static ssize_t snapshot_read(struct file *filp, char __user *buf,
91                              size_t count, loff_t *offp)
92 {
93 	struct snapshot_data *data;
94 	ssize_t res;
95 
96 	data = filp->private_data;
97 	res = snapshot_read_next(&data->handle, count);
98 	if (res > 0) {
99 		if (copy_to_user(buf, data_of(data->handle), res))
100 			res = -EFAULT;
101 		else
102 			*offp = data->handle.offset;
103 	}
104 	return res;
105 }
106 
107 static ssize_t snapshot_write(struct file *filp, const char __user *buf,
108                               size_t count, loff_t *offp)
109 {
110 	struct snapshot_data *data;
111 	ssize_t res;
112 
113 	data = filp->private_data;
114 	res = snapshot_write_next(&data->handle, count);
115 	if (res > 0) {
116 		if (copy_from_user(data_of(data->handle), buf, res))
117 			res = -EFAULT;
118 		else
119 			*offp = data->handle.offset;
120 	}
121 	return res;
122 }
123 
124 static int snapshot_ioctl(struct inode *inode, struct file *filp,
125                           unsigned int cmd, unsigned long arg)
126 {
127 	int error = 0;
128 	struct snapshot_data *data;
129 	loff_t offset, avail;
130 
131 	if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
132 		return -ENOTTY;
133 	if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
134 		return -ENOTTY;
135 	if (!capable(CAP_SYS_ADMIN))
136 		return -EPERM;
137 
138 	data = filp->private_data;
139 
140 	switch (cmd) {
141 
142 	case SNAPSHOT_FREEZE:
143 		if (data->frozen)
144 			break;
145 		down(&pm_sem);
146 		error = disable_nonboot_cpus();
147 		if (!error) {
148 			error = freeze_processes();
149 			if (error) {
150 				thaw_processes();
151 				enable_nonboot_cpus();
152 				error = -EBUSY;
153 			}
154 		}
155 		up(&pm_sem);
156 		if (!error)
157 			data->frozen = 1;
158 		break;
159 
160 	case SNAPSHOT_UNFREEZE:
161 		if (!data->frozen)
162 			break;
163 		down(&pm_sem);
164 		thaw_processes();
165 		enable_nonboot_cpus();
166 		up(&pm_sem);
167 		data->frozen = 0;
168 		break;
169 
170 	case SNAPSHOT_ATOMIC_SNAPSHOT:
171 		if (data->mode != O_RDONLY || !data->frozen  || data->ready) {
172 			error = -EPERM;
173 			break;
174 		}
175 		down(&pm_sem);
176 		/* Free memory before shutting down devices. */
177 		error = swsusp_shrink_memory();
178 		if (!error) {
179 			suspend_console();
180 			error = device_suspend(PMSG_FREEZE);
181 			if (!error) {
182 				in_suspend = 1;
183 				error = swsusp_suspend();
184 				device_resume();
185 			}
186 			resume_console();
187 		}
188 		up(&pm_sem);
189 		if (!error)
190 			error = put_user(in_suspend, (unsigned int __user *)arg);
191 		if (!error)
192 			data->ready = 1;
193 		break;
194 
195 	case SNAPSHOT_ATOMIC_RESTORE:
196 		if (data->mode != O_WRONLY || !data->frozen ||
197 		    !snapshot_image_loaded(&data->handle)) {
198 			error = -EPERM;
199 			break;
200 		}
201 		snapshot_free_unused_memory(&data->handle);
202 		down(&pm_sem);
203 		pm_prepare_console();
204 		suspend_console();
205 		error = device_suspend(PMSG_PRETHAW);
206 		if (!error) {
207 			error = swsusp_resume();
208 			device_resume();
209 		}
210 		resume_console();
211 		pm_restore_console();
212 		up(&pm_sem);
213 		break;
214 
215 	case SNAPSHOT_FREE:
216 		swsusp_free();
217 		memset(&data->handle, 0, sizeof(struct snapshot_handle));
218 		data->ready = 0;
219 		break;
220 
221 	case SNAPSHOT_SET_IMAGE_SIZE:
222 		image_size = arg;
223 		break;
224 
225 	case SNAPSHOT_AVAIL_SWAP:
226 		avail = count_swap_pages(data->swap, 1);
227 		avail <<= PAGE_SHIFT;
228 		error = put_user(avail, (loff_t __user *)arg);
229 		break;
230 
231 	case SNAPSHOT_GET_SWAP_PAGE:
232 		if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
233 			error = -ENODEV;
234 			break;
235 		}
236 		if (!data->bitmap) {
237 			data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0));
238 			if (!data->bitmap) {
239 				error = -ENOMEM;
240 				break;
241 			}
242 		}
243 		offset = alloc_swap_page(data->swap, data->bitmap);
244 		if (offset) {
245 			offset <<= PAGE_SHIFT;
246 			error = put_user(offset, (loff_t __user *)arg);
247 		} else {
248 			error = -ENOSPC;
249 		}
250 		break;
251 
252 	case SNAPSHOT_FREE_SWAP_PAGES:
253 		if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
254 			error = -ENODEV;
255 			break;
256 		}
257 		free_all_swap_pages(data->swap, data->bitmap);
258 		free_bitmap(data->bitmap);
259 		data->bitmap = NULL;
260 		break;
261 
262 	case SNAPSHOT_SET_SWAP_FILE:
263 		if (!data->bitmap) {
264 			/*
265 			 * User space encodes device types as two-byte values,
266 			 * so we need to recode them
267 			 */
268 			if (old_decode_dev(arg)) {
269 				data->swap = swap_type_of(old_decode_dev(arg), 0);
270 				if (data->swap < 0)
271 					error = -ENODEV;
272 			} else {
273 				data->swap = -1;
274 				error = -EINVAL;
275 			}
276 		} else {
277 			error = -EPERM;
278 		}
279 		break;
280 
281 	case SNAPSHOT_S2RAM:
282 		if (!data->frozen) {
283 			error = -EPERM;
284 			break;
285 		}
286 
287 		if (down_trylock(&pm_sem)) {
288 			error = -EBUSY;
289 			break;
290 		}
291 
292 		if (pm_ops->prepare) {
293 			error = pm_ops->prepare(PM_SUSPEND_MEM);
294 			if (error)
295 				goto OutS3;
296 		}
297 
298 		/* Put devices to sleep */
299 		suspend_console();
300 		error = device_suspend(PMSG_SUSPEND);
301 		if (error) {
302 			printk(KERN_ERR "Failed to suspend some devices.\n");
303 		} else {
304 			/* Enter S3, system is already frozen */
305 			suspend_enter(PM_SUSPEND_MEM);
306 
307 			/* Wake up devices */
308 			device_resume();
309 		}
310 		resume_console();
311 		if (pm_ops->finish)
312 			pm_ops->finish(PM_SUSPEND_MEM);
313 
314 OutS3:
315 		up(&pm_sem);
316 		break;
317 
318 	case SNAPSHOT_PMOPS:
319 		switch (arg) {
320 
321 		case PMOPS_PREPARE:
322 			if (pm_ops->prepare) {
323 				error = pm_ops->prepare(PM_SUSPEND_DISK);
324 			}
325 			break;
326 
327 		case PMOPS_ENTER:
328 			kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
329 			error = pm_ops->enter(PM_SUSPEND_DISK);
330 			break;
331 
332 		case PMOPS_FINISH:
333 			if (pm_ops && pm_ops->finish) {
334 				pm_ops->finish(PM_SUSPEND_DISK);
335 			}
336 			break;
337 
338 		default:
339 			printk(KERN_ERR "SNAPSHOT_PMOPS: invalid argument %ld\n", arg);
340 			error = -EINVAL;
341 
342 		}
343 		break;
344 
345 	default:
346 		error = -ENOTTY;
347 
348 	}
349 
350 	return error;
351 }
352 
353 static struct file_operations snapshot_fops = {
354 	.open = snapshot_open,
355 	.release = snapshot_release,
356 	.read = snapshot_read,
357 	.write = snapshot_write,
358 	.llseek = no_llseek,
359 	.ioctl = snapshot_ioctl,
360 };
361 
362 static struct miscdevice snapshot_device = {
363 	.minor = SNAPSHOT_MINOR,
364 	.name = "snapshot",
365 	.fops = &snapshot_fops,
366 };
367 
368 static int __init snapshot_device_init(void)
369 {
370 	return misc_register(&snapshot_device);
371 };
372 
373 device_initcall(snapshot_device_init);
374