Skip to content

Commit 880b957

Browse files
author
Darrick J. Wong
committed
fs: distinguish between user initiated freeze and kernel initiated freeze
Userspace can freeze a filesystem using the FIFREEZE ioctl or by suspending the block device; this state persists until userspace thaws the filesystem with the FITHAW ioctl or resuming the block device. Since commit 18e9e51 ("Introduce freeze_super and thaw_super for the fsfreeze ioctl") we only allow the first freeze command to succeed. The kernel may decide that it is necessary to freeze a filesystem for its own internal purposes, such as suspends in progress, filesystem fsck activities, or quiescing a device prior to removal. Userspace thaw commands must never break a kernel freeze, and kernel thaw commands shouldn't undo userspace's freeze command. Introduce a couple of freeze holder flags and wire it into the sb_writers state. One kernel and one userspace freeze are allowed to coexist at the same time; the filesystem will not thaw until both are lifted. I wonder if the f2fs/gfs2 code should be using a kernel freeze here, but for now we'll use FREEZE_HOLDER_USERSPACE to preserve existing behaviors. Cc: mcgrof@kernel.org Cc: jack@suse.cz Cc: hch@infradead.org Cc: ruansy.fnst@fujitsu.com Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Jan Kara <jack@suse.cz>
1 parent fdf0eaf commit 880b957

File tree

8 files changed

+106
-34
lines changed

8 files changed

+106
-34
lines changed

Documentation/filesystems/vfs.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,11 @@ filesystem. The following members are defined:
260260
void (*evict_inode) (struct inode *);
261261
void (*put_super) (struct super_block *);
262262
int (*sync_fs)(struct super_block *sb, int wait);
263-
int (*freeze_super) (struct super_block *);
263+
int (*freeze_super) (struct super_block *sb,
264+
enum freeze_holder who);
264265
int (*freeze_fs) (struct super_block *);
265-
int (*thaw_super) (struct super_block *);
266+
int (*thaw_super) (struct super_block *sb,
267+
enum freeze_wholder who);
266268
int (*unfreeze_fs) (struct super_block *);
267269
int (*statfs) (struct dentry *, struct kstatfs *);
268270
int (*remount_fs) (struct super_block *, int *, char *);

block/bdev.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,9 @@ int freeze_bdev(struct block_device *bdev)
248248
if (!sb)
249249
goto sync;
250250
if (sb->s_op->freeze_super)
251-
error = sb->s_op->freeze_super(sb);
251+
error = sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE);
252252
else
253-
error = freeze_super(sb);
253+
error = freeze_super(sb, FREEZE_HOLDER_USERSPACE);
254254
deactivate_super(sb);
255255

256256
if (error) {
@@ -291,9 +291,9 @@ int thaw_bdev(struct block_device *bdev)
291291
goto out;
292292

293293
if (sb->s_op->thaw_super)
294-
error = sb->s_op->thaw_super(sb);
294+
error = sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE);
295295
else
296-
error = thaw_super(sb);
296+
error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
297297
if (error)
298298
bdev->bd_fsfreeze_count++;
299299
else

fs/f2fs/gc.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,12 +2181,14 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
21812181
if (err)
21822182
return err;
21832183

2184-
err = freeze_super(sbi->sb);
2184+
err = freeze_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
21852185
if (err)
21862186
return err;
21872187

21882188
if (f2fs_readonly(sbi->sb)) {
2189-
thaw_super(sbi->sb);
2189+
err = thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
2190+
if (err)
2191+
return err;
21902192
return -EROFS;
21912193
}
21922194

@@ -2240,6 +2242,6 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
22402242
out_err:
22412243
f2fs_up_write(&sbi->cp_global_sem);
22422244
f2fs_up_write(&sbi->gc_lock);
2243-
thaw_super(sbi->sb);
2245+
thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
22442246
return err;
22452247
}

fs/gfs2/super.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -689,15 +689,17 @@ static int gfs2_freeze_locally(struct gfs2_sbd *sdp)
689689
struct super_block *sb = sdp->sd_vfs;
690690
int error;
691691

692-
error = freeze_super(sb);
692+
error = freeze_super(sb, FREEZE_HOLDER_USERSPACE);
693693
if (error)
694694
return error;
695695

696696
if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
697697
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE |
698698
GFS2_LFC_FREEZE_GO_SYNC);
699699
if (gfs2_withdrawn(sdp)) {
700-
thaw_super(sb);
700+
error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
701+
if (error)
702+
return error;
701703
return -EIO;
702704
}
703705
}
@@ -712,7 +714,7 @@ static int gfs2_do_thaw(struct gfs2_sbd *sdp)
712714
error = gfs2_freeze_lock_shared(sdp);
713715
if (error)
714716
goto fail;
715-
error = thaw_super(sb);
717+
error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
716718
if (!error)
717719
return 0;
718720

@@ -761,7 +763,7 @@ void gfs2_freeze_func(struct work_struct *work)
761763
*
762764
*/
763765

764-
static int gfs2_freeze_super(struct super_block *sb)
766+
static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
765767
{
766768
struct gfs2_sbd *sdp = sb->s_fs_info;
767769
int error;
@@ -816,7 +818,7 @@ static int gfs2_freeze_super(struct super_block *sb)
816818
*
817819
*/
818820

819-
static int gfs2_thaw_super(struct super_block *sb)
821+
static int gfs2_thaw_super(struct super_block *sb, enum freeze_holder who)
820822
{
821823
struct gfs2_sbd *sdp = sb->s_fs_info;
822824
int error;

fs/gfs2/sys.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
168168

169169
switch (n) {
170170
case 0:
171-
error = thaw_super(sdp->sd_vfs);
171+
error = thaw_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE);
172172
break;
173173
case 1:
174-
error = freeze_super(sdp->sd_vfs);
174+
error = freeze_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE);
175175
break;
176176
default:
177177
return -EINVAL;

fs/ioctl.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,8 @@ static int ioctl_fsfreeze(struct file *filp)
396396

397397
/* Freeze */
398398
if (sb->s_op->freeze_super)
399-
return sb->s_op->freeze_super(sb);
400-
return freeze_super(sb);
399+
return sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE);
400+
return freeze_super(sb, FREEZE_HOLDER_USERSPACE);
401401
}
402402

403403
static int ioctl_fsthaw(struct file *filp)
@@ -409,8 +409,8 @@ static int ioctl_fsthaw(struct file *filp)
409409

410410
/* Thaw */
411411
if (sb->s_op->thaw_super)
412-
return sb->s_op->thaw_super(sb);
413-
return thaw_super(sb);
412+
return sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE);
413+
return thaw_super(sb, FREEZE_HOLDER_USERSPACE);
414414
}
415415

416416
static int ioctl_file_dedupe_range(struct file *file,

fs/super.c

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#include <uapi/linux/mount.h>
4040
#include "internal.h"
4141

42-
static int thaw_super_locked(struct super_block *sb);
42+
static int thaw_super_locked(struct super_block *sb, enum freeze_holder who);
4343

4444
static LIST_HEAD(super_blocks);
4545
static DEFINE_SPINLOCK(sb_lock);
@@ -1030,7 +1030,7 @@ static void do_thaw_all_callback(struct super_block *sb)
10301030
down_write(&sb->s_umount);
10311031
if (sb->s_root && sb->s_flags & SB_BORN) {
10321032
emergency_thaw_bdev(sb);
1033-
thaw_super_locked(sb);
1033+
thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE);
10341034
} else {
10351035
up_write(&sb->s_umount);
10361036
}
@@ -1647,11 +1647,22 @@ static void sb_freeze_unlock(struct super_block *sb, int level)
16471647
/**
16481648
* freeze_super - lock the filesystem and force it into a consistent state
16491649
* @sb: the super to lock
1650+
* @who: context that wants to freeze
16501651
*
16511652
* Syncs the super to make sure the filesystem is consistent and calls the fs's
1652-
* freeze_fs. Subsequent calls to this without first thawing the fs will return
1653+
* freeze_fs. Subsequent calls to this without first thawing the fs may return
16531654
* -EBUSY.
16541655
*
1656+
* @who should be:
1657+
* * %FREEZE_HOLDER_USERSPACE if userspace wants to freeze the fs;
1658+
* * %FREEZE_HOLDER_KERNEL if the kernel wants to freeze the fs.
1659+
*
1660+
* The @who argument distinguishes between the kernel and userspace trying to
1661+
* freeze the filesystem. Although there cannot be multiple kernel freezes or
1662+
* multiple userspace freezes in effect at any given time, the kernel and
1663+
* userspace can both hold a filesystem frozen. The filesystem remains frozen
1664+
* until there are no kernel or userspace freezes in effect.
1665+
*
16551666
* During this function, sb->s_writers.frozen goes through these values:
16561667
*
16571668
* SB_UNFROZEN: File system is normal, all writes progress as usual.
@@ -1677,12 +1688,30 @@ static void sb_freeze_unlock(struct super_block *sb, int level)
16771688
*
16781689
* sb->s_writers.frozen is protected by sb->s_umount.
16791690
*/
1680-
int freeze_super(struct super_block *sb)
1691+
int freeze_super(struct super_block *sb, enum freeze_holder who)
16811692
{
16821693
int ret;
16831694

16841695
atomic_inc(&sb->s_active);
16851696
down_write(&sb->s_umount);
1697+
1698+
if (sb->s_writers.frozen == SB_FREEZE_COMPLETE) {
1699+
if (sb->s_writers.freeze_holders & who) {
1700+
deactivate_locked_super(sb);
1701+
return -EBUSY;
1702+
}
1703+
1704+
WARN_ON(sb->s_writers.freeze_holders == 0);
1705+
1706+
/*
1707+
* Someone else already holds this type of freeze; share the
1708+
* freeze and assign the active ref to the freeze.
1709+
*/
1710+
sb->s_writers.freeze_holders |= who;
1711+
up_write(&sb->s_umount);
1712+
return 0;
1713+
}
1714+
16861715
if (sb->s_writers.frozen != SB_UNFROZEN) {
16871716
deactivate_locked_super(sb);
16881717
return -EBUSY;
@@ -1695,6 +1724,7 @@ int freeze_super(struct super_block *sb)
16951724

16961725
if (sb_rdonly(sb)) {
16971726
/* Nothing to do really... */
1727+
sb->s_writers.freeze_holders |= who;
16981728
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
16991729
up_write(&sb->s_umount);
17001730
return 0;
@@ -1738,23 +1768,47 @@ int freeze_super(struct super_block *sb)
17381768
* For debugging purposes so that fs can warn if it sees write activity
17391769
* when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super().
17401770
*/
1771+
sb->s_writers.freeze_holders |= who;
17411772
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
17421773
lockdep_sb_freeze_release(sb);
17431774
up_write(&sb->s_umount);
17441775
return 0;
17451776
}
17461777
EXPORT_SYMBOL(freeze_super);
17471778

1748-
static int thaw_super_locked(struct super_block *sb)
1779+
/*
1780+
* Undoes the effect of a freeze_super_locked call. If the filesystem is
1781+
* frozen both by userspace and the kernel, a thaw call from either source
1782+
* removes that state without releasing the other state or unlocking the
1783+
* filesystem.
1784+
*/
1785+
static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)
17491786
{
17501787
int error;
17511788

1752-
if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) {
1789+
if (sb->s_writers.frozen == SB_FREEZE_COMPLETE) {
1790+
if (!(sb->s_writers.freeze_holders & who)) {
1791+
up_write(&sb->s_umount);
1792+
return -EINVAL;
1793+
}
1794+
1795+
/*
1796+
* Freeze is shared with someone else. Release our hold and
1797+
* drop the active ref that freeze_super assigned to the
1798+
* freezer.
1799+
*/
1800+
if (sb->s_writers.freeze_holders & ~who) {
1801+
sb->s_writers.freeze_holders &= ~who;
1802+
deactivate_locked_super(sb);
1803+
return 0;
1804+
}
1805+
} else {
17531806
up_write(&sb->s_umount);
17541807
return -EINVAL;
17551808
}
17561809

17571810
if (sb_rdonly(sb)) {
1811+
sb->s_writers.freeze_holders &= ~who;
17581812
sb->s_writers.frozen = SB_UNFROZEN;
17591813
goto out;
17601814
}
@@ -1772,6 +1826,7 @@ static int thaw_super_locked(struct super_block *sb)
17721826
}
17731827
}
17741828

1829+
sb->s_writers.freeze_holders &= ~who;
17751830
sb->s_writers.frozen = SB_UNFROZEN;
17761831
sb_freeze_unlock(sb, SB_FREEZE_FS);
17771832
out:
@@ -1782,13 +1837,19 @@ static int thaw_super_locked(struct super_block *sb)
17821837
/**
17831838
* thaw_super -- unlock filesystem
17841839
* @sb: the super to thaw
1840+
* @who: context that wants to freeze
1841+
*
1842+
* Unlocks the filesystem and marks it writeable again after freeze_super()
1843+
* if there are no remaining freezes on the filesystem.
17851844
*
1786-
* Unlocks the filesystem and marks it writeable again after freeze_super().
1845+
* @who should be:
1846+
* * %FREEZE_HOLDER_USERSPACE if userspace wants to thaw the fs;
1847+
* * %FREEZE_HOLDER_KERNEL if the kernel wants to thaw the fs.
17871848
*/
1788-
int thaw_super(struct super_block *sb)
1849+
int thaw_super(struct super_block *sb, enum freeze_holder who)
17891850
{
17901851
down_write(&sb->s_umount);
1791-
return thaw_super_locked(sb);
1852+
return thaw_super_locked(sb, who);
17921853
}
17931854
EXPORT_SYMBOL(thaw_super);
17941855

include/linux/fs.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,8 @@ enum {
11471147
#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1)
11481148

11491149
struct sb_writers {
1150-
int frozen; /* Is sb frozen? */
1150+
unsigned short frozen; /* Is sb frozen? */
1151+
unsigned short freeze_holders; /* Who froze fs? */
11511152
struct percpu_rw_semaphore rw_sem[SB_FREEZE_LEVELS];
11521153
};
11531154

@@ -1902,6 +1903,10 @@ extern loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
19021903
struct file *dst_file, loff_t dst_pos,
19031904
loff_t len, unsigned int remap_flags);
19041905

1906+
enum freeze_holder {
1907+
FREEZE_HOLDER_KERNEL = (1U << 0),
1908+
FREEZE_HOLDER_USERSPACE = (1U << 1),
1909+
};
19051910

19061911
struct super_operations {
19071912
struct inode *(*alloc_inode)(struct super_block *sb);
@@ -1914,9 +1919,9 @@ struct super_operations {
19141919
void (*evict_inode) (struct inode *);
19151920
void (*put_super) (struct super_block *);
19161921
int (*sync_fs)(struct super_block *sb, int wait);
1917-
int (*freeze_super) (struct super_block *);
1922+
int (*freeze_super) (struct super_block *, enum freeze_holder who);
19181923
int (*freeze_fs) (struct super_block *);
1919-
int (*thaw_super) (struct super_block *);
1924+
int (*thaw_super) (struct super_block *, enum freeze_holder who);
19201925
int (*unfreeze_fs) (struct super_block *);
19211926
int (*statfs) (struct dentry *, struct kstatfs *);
19221927
int (*remount_fs) (struct super_block *, int *, char *);
@@ -2290,8 +2295,8 @@ extern int unregister_filesystem(struct file_system_type *);
22902295
extern int vfs_statfs(const struct path *, struct kstatfs *);
22912296
extern int user_statfs(const char __user *, struct kstatfs *);
22922297
extern int fd_statfs(int, struct kstatfs *);
2293-
extern int freeze_super(struct super_block *super);
2294-
extern int thaw_super(struct super_block *super);
2298+
int freeze_super(struct super_block *super, enum freeze_holder who);
2299+
int thaw_super(struct super_block *super, enum freeze_holder who);
22952300
extern __printf(2, 3)
22962301
int super_setup_bdi_name(struct super_block *sb, char *fmt, ...);
22972302
extern int super_setup_bdi(struct super_block *sb);

0 commit comments

Comments
 (0)