linux/kernel/power/user.c
<<
>>
Prefs
   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/kmod.h>
  16#include <linux/string.h>
  17#include <linux/device.h>
  18#include <linux/miscdevice.h>
  19#include <linux/mm.h>
  20#include <linux/swap.h>
  21#include <linux/swapops.h>
  22#include <linux/pm.h>
  23#include <linux/fs.h>
  24#include <linux/compat.h>
  25#include <linux/console.h>
  26#include <linux/cpu.h>
  27#include <linux/freezer.h>
  28#include <scsi/scsi_scan.h>
  29
  30#include <asm/uaccess.h>
  31
  32#include "power.h"
  33
  34
  35#define SNAPSHOT_MINOR  231
  36
  37static struct snapshot_data {
  38        struct snapshot_handle handle;
  39        int swap;
  40        int mode;
  41        char frozen;
  42        char ready;
  43        char platform_support;
  44} snapshot_state;
  45
  46atomic_t snapshot_device_available = ATOMIC_INIT(1);
  47
  48static int snapshot_open(struct inode *inode, struct file *filp)
  49{
  50        struct snapshot_data *data;
  51        int error;
  52
  53        lock_system_sleep();
  54
  55        if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
  56                error = -EBUSY;
  57                goto Unlock;
  58        }
  59
  60        if ((filp->f_flags & O_ACCMODE) == O_RDWR) {
  61                atomic_inc(&snapshot_device_available);
  62                error = -ENOSYS;
  63                goto Unlock;
  64        }
  65        if(create_basic_memory_bitmaps()) {
  66                atomic_inc(&snapshot_device_available);
  67                error = -ENOMEM;
  68                goto Unlock;
  69        }
  70        nonseekable_open(inode, filp);
  71        data = &snapshot_state;
  72        filp->private_data = data;
  73        memset(&data->handle, 0, sizeof(struct snapshot_handle));
  74        if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
  75                /* Hibernating.  The image device should be accessible. */
  76                data->swap = swsusp_resume_device ?
  77                        swap_type_of(swsusp_resume_device, 0, NULL) : -1;
  78                data->mode = O_RDONLY;
  79                error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
  80                if (error)
  81                        pm_notifier_call_chain(PM_POST_HIBERNATION);
  82        } else {
  83                /*
  84                 * Resuming.  We may need to wait for the image device to
  85                 * appear.
  86                 */
  87                wait_for_device_probe();
  88                scsi_complete_async_scans();
  89
  90                data->swap = -1;
  91                data->mode = O_WRONLY;
  92                error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
  93                if (error)
  94                        pm_notifier_call_chain(PM_POST_RESTORE);
  95        }
  96        if (error) {
  97                free_basic_memory_bitmaps();
  98                atomic_inc(&snapshot_device_available);
  99        }
 100        data->frozen = 0;
 101        data->ready = 0;
 102        data->platform_support = 0;
 103
 104 Unlock:
 105        unlock_system_sleep();
 106
 107        return error;
 108}
 109
 110static int snapshot_release(struct inode *inode, struct file *filp)
 111{
 112        struct snapshot_data *data;
 113
 114        lock_system_sleep();
 115
 116        swsusp_free();
 117        free_basic_memory_bitmaps();
 118        data = filp->private_data;
 119        free_all_swap_pages(data->swap);
 120        if (data->frozen) {
 121                pm_restore_gfp_mask();
 122                thaw_processes();
 123        }
 124        pm_notifier_call_chain(data->mode == O_RDONLY ?
 125                        PM_POST_HIBERNATION : PM_POST_RESTORE);
 126        atomic_inc(&snapshot_device_available);
 127
 128        unlock_system_sleep();
 129
 130        return 0;
 131}
 132
 133static ssize_t snapshot_read(struct file *filp, char __user *buf,
 134                             size_t count, loff_t *offp)
 135{
 136        struct snapshot_data *data;
 137        ssize_t res;
 138        loff_t pg_offp = *offp & ~PAGE_MASK;
 139
 140        lock_system_sleep();
 141
 142        data = filp->private_data;
 143        if (!data->ready) {
 144                res = -ENODATA;
 145                goto Unlock;
 146        }
 147        if (!pg_offp) { /* on page boundary? */
 148                res = snapshot_read_next(&data->handle);
 149                if (res <= 0)
 150                        goto Unlock;
 151        } else {
 152                res = PAGE_SIZE - pg_offp;
 153        }
 154
 155        res = simple_read_from_buffer(buf, count, &pg_offp,
 156                        data_of(data->handle), res);
 157        if (res > 0)
 158                *offp += res;
 159
 160 Unlock:
 161        unlock_system_sleep();
 162
 163        return res;
 164}
 165
 166static ssize_t snapshot_write(struct file *filp, const char __user *buf,
 167                              size_t count, loff_t *offp)
 168{
 169        struct snapshot_data *data;
 170        ssize_t res;
 171        loff_t pg_offp = *offp & ~PAGE_MASK;
 172
 173        lock_system_sleep();
 174
 175        data = filp->private_data;
 176
 177        if (!pg_offp) {
 178                res = snapshot_write_next(&data->handle);
 179                if (res <= 0)
 180                        goto unlock;
 181        } else {
 182                res = PAGE_SIZE - pg_offp;
 183        }
 184
 185        res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp,
 186                        buf, count);
 187        if (res > 0)
 188                *offp += res;
 189unlock:
 190        unlock_system_sleep();
 191
 192        return res;
 193}
 194
 195static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 196                                                        unsigned long arg)
 197{
 198        int error = 0;
 199        struct snapshot_data *data;
 200        loff_t size;
 201        sector_t offset;
 202
 203        if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
 204                return -ENOTTY;
 205        if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
 206                return -ENOTTY;
 207        if (!capable(CAP_SYS_ADMIN))
 208                return -EPERM;
 209
 210        if (!mutex_trylock(&pm_mutex))
 211                return -EBUSY;
 212
 213        data = filp->private_data;
 214
 215        switch (cmd) {
 216
 217        case SNAPSHOT_FREEZE:
 218                if (data->frozen)
 219                        break;
 220
 221                printk("Syncing filesystems ... ");
 222                sys_sync();
 223                printk("done.\n");
 224
 225                error = usermodehelper_disable();
 226                if (error)
 227                        break;
 228
 229                error = freeze_processes();
 230                if (error)
 231                        usermodehelper_enable();
 232                else
 233                        data->frozen = 1;
 234                break;
 235
 236        case SNAPSHOT_UNFREEZE:
 237                if (!data->frozen || data->ready)
 238                        break;
 239                pm_restore_gfp_mask();
 240                thaw_processes();
 241                usermodehelper_enable();
 242                data->frozen = 0;
 243                break;
 244
 245        case SNAPSHOT_CREATE_IMAGE:
 246                if (data->mode != O_RDONLY || !data->frozen  || data->ready) {
 247                        error = -EPERM;
 248                        break;
 249                }
 250                pm_restore_gfp_mask();
 251                error = hibernation_snapshot(data->platform_support);
 252                if (error) {
 253                        thaw_kernel_threads();
 254                } else {
 255                        error = put_user(in_suspend, (int __user *)arg);
 256                        if (!error && !freezer_test_done)
 257                                data->ready = 1;
 258                        if (freezer_test_done) {
 259                                freezer_test_done = false;
 260                                thaw_kernel_threads();
 261                        }
 262                }
 263                break;
 264
 265        case SNAPSHOT_ATOMIC_RESTORE:
 266                snapshot_write_finalize(&data->handle);
 267                if (data->mode != O_WRONLY || !data->frozen ||
 268                    !snapshot_image_loaded(&data->handle)) {
 269                        error = -EPERM;
 270                        break;
 271                }
 272                error = hibernation_restore(data->platform_support);
 273                break;
 274
 275        case SNAPSHOT_FREE:
 276                swsusp_free();
 277                memset(&data->handle, 0, sizeof(struct snapshot_handle));
 278                data->ready = 0;
 279                /*
 280                 * It is necessary to thaw kernel threads here, because
 281                 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
 282                 * SNAPSHOT_FREE.  In that case, if kernel threads were not
 283                 * thawed, the preallocation of memory carried out by
 284                 * hibernation_snapshot() might run into problems (i.e. it
 285                 * might fail or even deadlock).
 286                 */
 287                thaw_kernel_threads();
 288                break;
 289
 290        case SNAPSHOT_PREF_IMAGE_SIZE:
 291                image_size = arg;
 292                break;
 293
 294        case SNAPSHOT_GET_IMAGE_SIZE:
 295                if (!data->ready) {
 296                        error = -ENODATA;
 297                        break;
 298                }
 299                size = snapshot_get_image_size();
 300                size <<= PAGE_SHIFT;
 301                error = put_user(size, (loff_t __user *)arg);
 302                break;
 303
 304        case SNAPSHOT_AVAIL_SWAP_SIZE:
 305                size = count_swap_pages(data->swap, 1);
 306                size <<= PAGE_SHIFT;
 307                error = put_user(size, (loff_t __user *)arg);
 308                break;
 309
 310        case SNAPSHOT_ALLOC_SWAP_PAGE:
 311                if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
 312                        error = -ENODEV;
 313                        break;
 314                }
 315                offset = alloc_swapdev_block(data->swap);
 316                if (offset) {
 317                        offset <<= PAGE_SHIFT;
 318                        error = put_user(offset, (loff_t __user *)arg);
 319                } else {
 320                        error = -ENOSPC;
 321                }
 322                break;
 323
 324        case SNAPSHOT_FREE_SWAP_PAGES:
 325                if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
 326                        error = -ENODEV;
 327                        break;
 328                }
 329                free_all_swap_pages(data->swap);
 330                break;
 331
 332        case SNAPSHOT_S2RAM:
 333                if (!data->frozen) {
 334                        error = -EPERM;
 335                        break;
 336                }
 337                /*
 338                 * Tasks are frozen and the notifiers have been called with
 339                 * PM_HIBERNATION_PREPARE
 340                 */
 341                error = suspend_devices_and_enter(PM_SUSPEND_MEM);
 342                data->ready = 0;
 343                break;
 344
 345        case SNAPSHOT_PLATFORM_SUPPORT:
 346                data->platform_support = !!arg;
 347                break;
 348
 349        case SNAPSHOT_POWER_OFF:
 350                if (data->platform_support)
 351                        error = hibernation_platform_enter();
 352                break;
 353
 354        case SNAPSHOT_SET_SWAP_AREA:
 355                if (swsusp_swap_in_use()) {
 356                        error = -EPERM;
 357                } else {
 358                        struct resume_swap_area swap_area;
 359                        dev_t swdev;
 360
 361                        error = copy_from_user(&swap_area, (void __user *)arg,
 362                                        sizeof(struct resume_swap_area));
 363                        if (error) {
 364                                error = -EFAULT;
 365                                break;
 366                        }
 367
 368                        /*
 369                         * User space encodes device types as two-byte values,
 370                         * so we need to recode them
 371                         */
 372                        swdev = new_decode_dev(swap_area.dev);
 373                        if (swdev) {
 374                                offset = swap_area.offset;
 375                                data->swap = swap_type_of(swdev, offset, NULL);
 376                                if (data->swap < 0)
 377                                        error = -ENODEV;
 378                        } else {
 379                                data->swap = -1;
 380                                error = -EINVAL;
 381                        }
 382                }
 383                break;
 384
 385        default:
 386                error = -ENOTTY;
 387
 388        }
 389
 390        mutex_unlock(&pm_mutex);
 391
 392        return error;
 393}
 394
 395#ifdef CONFIG_COMPAT
 396
 397struct compat_resume_swap_area {
 398        compat_loff_t offset;
 399        u32 dev;
 400} __packed;
 401
 402static long
 403snapshot_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 404{
 405        BUILD_BUG_ON(sizeof(loff_t) != sizeof(compat_loff_t));
 406
 407        switch (cmd) {
 408        case SNAPSHOT_GET_IMAGE_SIZE:
 409        case SNAPSHOT_AVAIL_SWAP_SIZE:
 410        case SNAPSHOT_ALLOC_SWAP_PAGE: {
 411                compat_loff_t __user *uoffset = compat_ptr(arg);
 412                loff_t offset;
 413                mm_segment_t old_fs;
 414                int err;
 415
 416                old_fs = get_fs();
 417                set_fs(KERNEL_DS);
 418                err = snapshot_ioctl(file, cmd, (unsigned long) &offset);
 419                set_fs(old_fs);
 420                if (!err && put_user(offset, uoffset))
 421                        err = -EFAULT;
 422                return err;
 423        }
 424
 425        case SNAPSHOT_CREATE_IMAGE:
 426                return snapshot_ioctl(file, cmd,
 427                                      (unsigned long) compat_ptr(arg));
 428
 429        case SNAPSHOT_SET_SWAP_AREA: {
 430                struct compat_resume_swap_area __user *u_swap_area =
 431                        compat_ptr(arg);
 432                struct resume_swap_area swap_area;
 433                mm_segment_t old_fs;
 434                int err;
 435
 436                err = get_user(swap_area.offset, &u_swap_area->offset);
 437                err |= get_user(swap_area.dev, &u_swap_area->dev);
 438                if (err)
 439                        return -EFAULT;
 440                old_fs = get_fs();
 441                set_fs(KERNEL_DS);
 442                err = snapshot_ioctl(file, SNAPSHOT_SET_SWAP_AREA,
 443                                     (unsigned long) &swap_area);
 444                set_fs(old_fs);
 445                return err;
 446        }
 447
 448        default:
 449                return snapshot_ioctl(file, cmd, arg);
 450        }
 451}
 452
 453#endif /* CONFIG_COMPAT */
 454
 455static const struct file_operations snapshot_fops = {
 456        .open = snapshot_open,
 457        .release = snapshot_release,
 458        .read = snapshot_read,
 459        .write = snapshot_write,
 460        .llseek = no_llseek,
 461        .unlocked_ioctl = snapshot_ioctl,
 462#ifdef CONFIG_COMPAT
 463        .compat_ioctl = snapshot_compat_ioctl,
 464#endif
 465};
 466
 467static struct miscdevice snapshot_device = {
 468        .minor = SNAPSHOT_MINOR,
 469        .name = "snapshot",
 470        .fops = &snapshot_fops,
 471};
 472
 473static int __init snapshot_device_init(void)
 474{
 475        return misc_register(&snapshot_device);
 476};
 477
 478device_initcall(snapshot_device_init);
 479