Re: Regression: hibernation is broken since e6bc9de714972cac34daa1dc1567ee48a47a9342

Darrick J. Wong
2020-02-22T00:23:28Z

On Fri, Feb 14, 2020 at 10:15:24PM +0100, Domenico Andreoli wrote:
> [ added linux-pm ]
>
> On Thu, Feb 13, 2020 at 11:41:35AM -0800, Darrick J. Wong wrote:
> > On Thu, Feb 13, 2020 at 11:34:10AM -0800, Darrick J. Wong wrote:
> > >
> > > Well ... you could try the in-kernel hibernate (which I think is what
> > > 'systemctl hibernate' does), though you'd lose the nifty features of
> > > µswsusp.
>
> Indeed 'systemctl hibernate' works perfectly with v5.6-rc1 in my setup.
>
> > > In the end, though, I'll probably have to revert all those IS_SWAPFILE
> > > checks (at least if CONFIG_HIBERNATION=y) since it's not fair to force
> > > you to totally reconfigure your hibernation setup.
> >
> > Also, does the following partial revert fix uswsusp for you? It'll
> > allow the direct writes that uswsusp wants to do, while leaving the rest
> > (mmap writes) in place.
> >
> > --D
> >
> > diff --git a/fs/block_dev.c b/fs/block_dev.c
> > index 69bf2fb6f7cd..077d9fa6b87d 100644
> > --- a/fs/block_dev.c
> > +++ b/fs/block_dev.c
> > @@ -2001,8 +2001,10 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
> > if (bdev_read_only(I_BDEV(bd_inode)))
> > return -EPERM;
> >
> > +#ifndef CONFIG_HIBERNATION
> > if (IS_SWAPFILE(bd_inode))
> > return -ETXTBSY;
> > +#endif
>
> This alone is enough to make uswsusp work again.
>
> I propose this alternative:
>
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -2001,7 +2001,8 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
> if (bdev_read_only(I_BDEV(bd_inode)))
> return -EPERM;
>
> - if (IS_SWAPFILE(bd_inode))
> + /* Hibernation might happen via uswsusp, let it write to the swap */
> + if (IS_SWAPFILE(bd_inode) && !IS_ENABLED(CONFIG_HIBERNATION))
> return -ETXTBSY;
>
> if (!iov_iter_count(from))
>
> I looked for a more selective way to enable writes to swap at runtime,
> so I tried with system_entering_hibernation() but it's not yet armed
> at the point in which uswsusp wants to write to the swap and therefore
> it does not work.
>
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -34,6 +34,7 @@
> #include <linux/task_io_accounting_ops.h>
> #include <linux/falloc.h>
> #include <linux/uaccess.h>
> +#include <linux/suspend.h>
> #include "internal.h"
>
> struct bdev_inode {
> @@ -2001,7 +2002,8 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
> if (bdev_read_only(I_BDEV(bd_inode)))
> return -EPERM;
>
> - if (IS_SWAPFILE(bd_inode))
> + /* Hibernation might happen via uswsusp, let it write to the swap */
> + if (IS_SWAPFILE(bd_inode) && !system_entering_hibernation())
> return -ETXTBSY;
>
> if (!iov_iter_count(from))
>
> > if (!iov_iter_count(from))
> > return 0;
> > diff --git a/mm/filemap.c b/mm/filemap.c
> > index 1784478270e1..3df3211abe25 100644
> > --- a/mm/filemap.c
> > +++ b/mm/filemap.c
> > @@ -2920,8 +2920,10 @@ inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from)
> > loff_t count;
> > int ret;
> >
> > +#ifndef CONFIG_HIBERNATION
> > if (IS_SWAPFILE(inode))
> > return -ETXTBSY;
> > +#endif
> >
> > if (!iov_iter_count(from))
> > return 0;
>
> The above is not needed in my case but I'm not sure it would not be
> needed in some other configuration of uswsusp.

Ok, third try. Does the following work? This is a little more
selective in that it only disables the write protection on the swap
device/file that uswusp is going to write to.

--D

diff --git a/kernel/power/user.c b/kernel/power/user.c
index 77438954cc2b..a3ae9cbbfcf0 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -372,10 +372,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
*/
swdev = new_decode_dev(swap_area.dev);
if (swdev) {
+ struct block_device *bd;
+
offset = swap_area.offset;
- data->swap = swap_type_of(swdev, offset, NULL);
+ data->swap = swap_type_of(swdev, offset, &bd);
if (data->swap < 0)
error = -ENODEV;
+
+ inode_lock(bd->bd_inode);
+ bd->bd_inode->i_flags &= ~S_SWAPFILE;
+ inode_unlock(bd->bd_inode);
+ bdput(bd);
} else {
data->swap = -1;
error = -EINVAL;