From: Christoph Hellwig These are the generic lockfs bits. Basically it takes the XFS freezing statemachine into the VFS. It's all behind the kernel-doc documented freeze_bdev and thaw_bdev interfaces. Based on an older patch from Chris Mason. --- 25-akpm/fs/block_dev.c | 1 25-akpm/fs/buffer.c | 71 ++++++++++++++++++++++++++++++++++++ 25-akpm/fs/super.c | 8 ++++ 25-akpm/include/linux/buffer_head.h | 2 + 25-akpm/include/linux/fs.h | 16 ++++++++ 5 files changed, 98 insertions(+) diff -puN fs/block_dev.c~lockfs-vfs-bits fs/block_dev.c --- 25/fs/block_dev.c~lockfs-vfs-bits 2004-04-17 21:34:06.436750144 -0700 +++ 25-akpm/fs/block_dev.c 2004-04-17 21:34:06.446748624 -0700 @@ -251,6 +251,7 @@ static void init_once(void * foo, kmem_c { memset(bdev, 0, sizeof(*bdev)); sema_init(&bdev->bd_sem, 1); + sema_init(&bdev->bd_mount_sem, 1); INIT_LIST_HEAD(&bdev->bd_inodes); INIT_LIST_HEAD(&bdev->bd_list); inode_init_once(&ei->vfs_inode); diff -puN fs/buffer.c~lockfs-vfs-bits fs/buffer.c --- 25/fs/buffer.c~lockfs-vfs-bits 2004-04-17 21:34:06.438749840 -0700 +++ 25-akpm/fs/buffer.c 2004-04-17 21:34:06.449748168 -0700 @@ -227,6 +227,77 @@ int fsync_bdev(struct block_device *bdev return sync_blockdev(bdev); } +/** + * freeze_bdev -- lock a filesystem and force it into a consistent state + * @bdev: blockdevice to lock + * + * This takes the block device bd_mount_sem to make sure no new mounts + * happen on bdev until thaw_bdev() is called. + * If a superblock is found on this device, we take the s_umount semaphore + * on it to make sure nobody unmounts until the snapshot creation is done. + */ +struct super_block *freeze_bdev(struct block_device *bdev) +{ + struct super_block *sb; + + down(&bdev->bd_mount_sem); + sb = get_super(bdev); + if (sb && !(sb->s_flags & MS_RDONLY)) { + sb->s_frozen = SB_FREEZE_WRITE; + wmb(); + + sync_inodes_sb(sb, 0); + DQUOT_SYNC(sb); + + lock_super(sb); + if (sb->s_dirt && sb->s_op->write_super) + sb->s_op->write_super(sb); + unlock_super(sb); + + if (sb->s_op->sync_fs) + sb->s_op->sync_fs(sb, 1); + + sync_blockdev(sb->s_bdev); + sync_inodes_sb(sb, 1); + + sb->s_frozen = SB_FREEZE_TRANS; + wmb(); + + sync_blockdev(sb->s_bdev); + + if (sb->s_op->write_super_lockfs) + sb->s_op->write_super_lockfs(sb); + } + + sync_blockdev(bdev); + return sb; /* thaw_bdev releases s->s_umount and bd_mount_sem */ +} +EXPORT_SYMBOL(freeze_bdev); + +/** + * thaw_bdev -- unlock filesystem + * @bdev: blockdevice to unlock + * @sb: associated superblock + * + * Unlocks the filesystem and marks it writeable again after freeze_bdev(). + */ +void thaw_bdev(struct block_device *bdev, struct super_block *sb) +{ + if (sb) { + BUG_ON(sb->s_bdev != bdev); + + if (sb->s_op->unlockfs) + sb->s_op->unlockfs(sb); + sb->s_frozen = SB_UNFROZEN; + wmb(); + wake_up(&sb->s_wait_unfrozen); + drop_super(sb); + } + + up(&bdev->bd_mount_sem); +} +EXPORT_SYMBOL(thaw_bdev); + /* * sync everything. Start out by waking pdflush, because that writes back * all queues in parallel. diff -puN fs/super.c~lockfs-vfs-bits fs/super.c --- 25/fs/super.c~lockfs-vfs-bits 2004-04-17 21:34:06.440749536 -0700 +++ 25-akpm/fs/super.c 2004-04-17 21:34:06.450748016 -0700 @@ -78,6 +78,7 @@ static struct super_block *alloc_super(v sema_init(&s->s_dquot.dqio_sem, 1); sema_init(&s->s_dquot.dqonoff_sem, 1); init_rwsem(&s->s_dquot.dqptr_sem); + init_waitqueue_head(&s->s_wait_unfrozen); s->s_maxbytes = MAX_NON_LFS; s->dq_op = sb_dquot_ops; s->s_qcop = sb_quotactl_ops; @@ -624,7 +625,14 @@ struct super_block *get_sb_bdev(struct f if (IS_ERR(bdev)) return (struct super_block *)bdev; + /* + * once the super is inserted into the list by sget, s_umount + * will protect the lockfs code from trying to start a snapshot + * while we are mounting + */ + down(&bdev->bd_mount_sem); s = sget(fs_type, test_bdev_super, set_bdev_super, bdev); + up(&bdev->bd_mount_sem); if (IS_ERR(s)) goto out; diff -puN include/linux/buffer_head.h~lockfs-vfs-bits include/linux/buffer_head.h --- 25/include/linux/buffer_head.h~lockfs-vfs-bits 2004-04-17 21:34:06.441749384 -0700 +++ 25-akpm/include/linux/buffer_head.h 2004-04-17 21:34:06.450748016 -0700 @@ -157,6 +157,8 @@ void __wait_on_buffer(struct buffer_head wait_queue_head_t *bh_waitq_head(struct buffer_head *bh); void wake_up_buffer(struct buffer_head *bh); int fsync_bdev(struct block_device *); +struct super_block *freeze_bdev(struct block_device *); +void thaw_bdev(struct block_device *, struct super_block *); int fsync_super(struct super_block *); int fsync_no_super(struct block_device *); struct buffer_head *__find_get_block(struct block_device *, sector_t, int); diff -puN include/linux/fs.h~lockfs-vfs-bits include/linux/fs.h --- 25/include/linux/fs.h~lockfs-vfs-bits 2004-04-17 21:34:06.443749080 -0700 +++ 25-akpm/include/linux/fs.h 2004-04-17 21:34:06.452747712 -0700 @@ -345,6 +345,7 @@ struct block_device { struct inode * bd_inode; /* will die */ int bd_openers; struct semaphore bd_sem; /* open/close mutex */ + struct semaphore bd_mount_sem; /* mount mutex */ struct list_head bd_inodes; void * bd_holder; int bd_holders; @@ -751,6 +752,9 @@ struct super_block { struct list_head s_instances; struct quota_info s_dquot; /* Diskquota specific options */ + int s_frozen; + wait_queue_head_t s_wait_unfrozen; + char s_id[32]; /* Informational name */ void *s_fs_info; /* Filesystem private info */ @@ -763,6 +767,18 @@ struct super_block { }; /* + * Snapshotting support. + */ +enum { + SB_UNFROZEN = 0, + SB_FREEZE_WRITE = 1, + SB_FREEZE_TRANS = 2, +}; + +#define vfs_check_frozen(sb, level) \ + wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) + +/* * Superblock locking. */ static inline void lock_super(struct super_block * sb) _