VFS lock patch. [Chris Mason] --- diff/fs/block_dev.c 2004-02-27 11:30:14.000000000 +0000 +++ source/fs/block_dev.c 2004-02-27 11:30:21.000000000 +0000 @@ -242,6 +242,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/fs/buffer.c 2004-02-23 13:56:46.000000000 +0000 +++ source/fs/buffer.c 2004-02-27 11:30:21.000000000 +0000 @@ -259,6 +259,17 @@ int fsync_bdev(struct block_device *bdev return sync_blockdev(bdev); } +int fsync_bdev_lockfs(struct block_device *bdev) +{ + int res; + res = fsync_bdev(bdev); + if (res) + return res; + sync_super_lockfs(bdev); + return sync_blockdev(bdev); +} +EXPORT_SYMBOL(fsync_bdev_lockfs); + /* * sync everything. Start out by waking pdflush, because that writes back * all queues in parallel. --- diff/fs/reiserfs/super.c 2004-01-19 10:22:59.000000000 +0000 +++ source/fs/reiserfs/super.c 2004-02-27 11:30:21.000000000 +0000 @@ -82,7 +82,7 @@ static void reiserfs_write_super_lockfs reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1); journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); reiserfs_block_writes(&th) ; - journal_end(&th, s, 1) ; + journal_end_sync(&th, s, 1) ; } s->s_dirt = dirty; reiserfs_write_unlock(s); --- diff/fs/super.c 2004-02-23 13:56:46.000000000 +0000 +++ source/fs/super.c 2004-02-27 11:30:21.000000000 +0000 @@ -293,6 +293,62 @@ static inline void write_super(struct su } /* + * triggered by the device mapper code to lock a filesystem and force + * it into a consistent state. + * + * This takes the block device bd_mount_sem to make sure no new mounts + * happen on bdev until unlockfs is called. If a super is found on this + * block device, we hould a read lock on the s->s_umount sem to make sure + * nobody unmounts until the snapshot creation is done + */ +void sync_super_lockfs(struct block_device *bdev) +{ + struct super_block *sb; + down(&bdev->bd_mount_sem); + sb = get_super(bdev); + if (sb) { + lock_super(sb); + if (sb->s_dirt && sb->s_op->write_super) + sb->s_op->write_super(sb); + if (sb->s_op->write_super_lockfs) + sb->s_op->write_super_lockfs(sb); + unlock_super(sb); + } + /* unlockfs releases s->s_umount and bd_mount_sem */ +} + +void unlockfs(struct block_device *bdev) +{ + struct list_head *p; + /* + * copied from get_super, but we need to + * do special things since lockfs left the + * s_umount sem held + */ + spin_lock(&sb_lock); + list_for_each(p, &super_blocks) { + struct super_block *s = sb_entry(p); + /* + * if there is a super for this block device + * in the list, get_super must have found it + * during sync_super_lockfs, so our drop_super + * will drop the reference created there. + */ + if (s->s_bdev == bdev && s->s_root) { + spin_unlock(&sb_lock); + if (s->s_op->unlockfs) + s->s_op->unlockfs(s); + drop_super(s); + goto unlock; + } + } + spin_unlock(&sb_lock); +unlock: + up(&bdev->bd_mount_sem); +} +EXPORT_SYMBOL(unlockfs); + +/* * Note: check the dirty flag before waiting, so we don't * hold up the sync while mounting a device. (The newly * mounted device won't need syncing.) @@ -622,7 +678,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/include/linux/buffer_head.h 2004-02-09 10:36:12.000000000 +0000 +++ source/include/linux/buffer_head.h 2004-02-27 11:30:21.000000000 +0000 @@ -164,6 +164,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 *); +int fsync_bdev_lockfs(struct block_device *); +void unlockfs(struct block_device *); 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/include/linux/fs.h 2004-02-23 13:56:47.000000000 +0000 +++ source/include/linux/fs.h 2004-02-27 11:30:21.000000000 +0000 @@ -346,6 +346,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; @@ -1220,6 +1221,7 @@ extern int filemap_flush(struct address_ extern int filemap_fdatawait(struct address_space *); extern int filemap_write_and_wait(struct address_space *mapping); extern void sync_supers(void); +extern void sync_super_lockfs(struct block_device *); extern void sync_filesystems(int wait); extern void emergency_sync(void); extern void emergency_remount(void);