summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/block_dev.c3
-rw-r--r--include/linux/suspend.h6
-rw-r--r--kernel/power/user.c14
3 files changed, 20 insertions, 3 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 93672c3f1c78..608a7b9173f4 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -2023,8 +2023,7 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (bdev_read_only(I_BDEV(bd_inode)))
return -EPERM;
- /* uswsusp needs write permission to the swap */
- if (IS_SWAPFILE(bd_inode) && !hibernation_available())
+ if (IS_SWAPFILE(bd_inode) && !is_hibernate_resume_dev(bd_inode))
return -ETXTBSY;
if (!iov_iter_count(from))
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 4fcc6fd0cbd6..b960098acfb0 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -466,6 +466,12 @@ static inline bool system_entering_hibernation(void) { return false; }
static inline bool hibernation_available(void) { return false; }
#endif /* CONFIG_HIBERNATION */
+#ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV
+int is_hibernate_resume_dev(const struct inode *);
+#else
+static inline int is_hibernate_resume_dev(const struct inode *i) { return 0; }
+#endif
+
/* Hibernation and suspend events */
#define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */
#define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 98548d1cf8a6..d5eedc2baa2a 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -35,8 +35,14 @@ static struct snapshot_data {
bool ready;
bool platform_support;
bool free_bitmaps;
+ struct inode *bd_inode;
} snapshot_state;
+int is_hibernate_resume_dev(const struct inode *bd_inode)
+{
+ return hibernation_available() && snapshot_state.bd_inode == bd_inode;
+}
+
static int snapshot_open(struct inode *inode, struct file *filp)
{
struct snapshot_data *data;
@@ -95,6 +101,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
data->frozen = false;
data->ready = false;
data->platform_support = false;
+ data->bd_inode = NULL;
Unlock:
unlock_system_sleep();
@@ -110,6 +117,7 @@ static int snapshot_release(struct inode *inode, struct file *filp)
swsusp_free();
data = filp->private_data;
+ data->bd_inode = NULL;
free_all_swap_pages(data->swap);
if (data->frozen) {
pm_restore_gfp_mask();
@@ -202,6 +210,7 @@ struct compat_resume_swap_area {
static int snapshot_set_swap_area(struct snapshot_data *data,
void __user *argp)
{
+ struct block_device *bdev;
sector_t offset;
dev_t swdev;
@@ -232,9 +241,12 @@ static int snapshot_set_swap_area(struct snapshot_data *data,
data->swap = -1;
return -EINVAL;
}
- data->swap = swap_type_of(swdev, offset, NULL);
+ data->swap = swap_type_of(swdev, offset, &bdev);
if (data->swap < 0)
return -ENODEV;
+
+ data->bd_inode = bdev->bd_inode;
+ bdput(bdev);
return 0;
}