Commit 75e9c9e1 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] death of is_mounted() and aother fixes

*	new functions - bd_claim(bdev, holder) and bd_release(bdev).
	bd_claim(bdev, holder) fails is device is already claimed by
	somebody else; bd_release(bdev) gives device up.

*	get_sb_bdev() claims device for fs_type; it means that we don't need
	to look through entire least of superblocks anymore - just through
	the list of superblocks belonging to that type (i.e. the same thing
	we do for non-block filesystems; that will allow to merge quite a
	bit of code afterwards).

*	sys_swapon claims device for itself;  free exclusion with mounting,
	end of problems with bogus set_blocksize().

*	is_mounted() and is_swap_partition() are gone - what we actually
	wanted was "try to claim device for ourselves".  Which we can do
	now - without races inherent to is_mounted()/is_swap_partition().

*	RAID lock_rdev() claims device for itself.  I.e. we get rid of
	is_mounted() in there (BTW, is_swap_partition() was missing) and
	we get protection both ways - not only RAID won't take an already
	mounted device, but mount won't stomp on a device claimed by RAID.

There are other places that would benefit from the same (e.g. ext3 with
external journal almost definitely wants to claim device for itself).

Notice that it's a cooperative thing - neither open() nor raw device stuff
claim the block device, so they don't care if device is mounted, etc.  So
we don't break fsck and friends - exclusion is between those who know that
they want that exclusion.
parent d9036aaf
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <linux/blk.h> /* for set_device_ro() */ #include <linux/blk.h> /* for set_device_ro() */
#include <linux/blkpg.h> #include <linux/blkpg.h>
#include <linux/genhd.h> #include <linux/genhd.h>
#include <linux/swap.h> /* for is_swap_partition() */
#include <linux/module.h> /* for EXPORT_SYMBOL */ #include <linux/module.h> /* for EXPORT_SYMBOL */
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -132,7 +131,9 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p) ...@@ -132,7 +131,9 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p)
kdev_t dev = to_kdev_t(bdev->bd_dev); kdev_t dev = to_kdev_t(bdev->bd_dev);
struct gendisk *g; struct gendisk *g;
kdev_t devp; kdev_t devp;
struct block_device *bdevp;
int drive, first_minor, minor; int drive, first_minor, minor;
int holder;
/* find the drive major */ /* find the drive major */
g = get_gendisk(dev); g = get_gendisk(dev);
...@@ -155,17 +156,24 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p) ...@@ -155,17 +156,24 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p)
/* partition in use? Incomplete check for now. */ /* partition in use? Incomplete check for now. */
devp = mk_kdev(major(dev), minor); devp = mk_kdev(major(dev), minor);
if (is_mounted(devp) || is_swap_partition(devp)) bdevp = bdget(kdev_t_to_nr(devp));
if (!bdevp)
return -ENOMEM;
if (bd_claim(bdevp, &holder) < 0) {
bdput(bdevp);
return -EBUSY; return -EBUSY;
}
/* all seems OK */ /* all seems OK */
fsync_dev(devp); fsync_bdev(bdevp);
invalidate_buffers(devp); invalidate_bdev(bdevp, 0);
g->part[minor].start_sect = 0; g->part[minor].start_sect = 0;
g->part[minor].nr_sects = 0; g->part[minor].nr_sects = 0;
if (g->sizes) if (g->sizes)
g->sizes[minor] = 0; g->sizes[minor] = 0;
bd_release(bdevp);
bdput(bdevp);
return 0; return 0;
} }
...@@ -210,6 +218,7 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) ...@@ -210,6 +218,7 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
int intval; int intval;
unsigned short usval; unsigned short usval;
kdev_t dev = to_kdev_t(bdev->bd_dev); kdev_t dev = to_kdev_t(bdev->bd_dev);
int holder;
intval = block_ioctl(dev, cmd, arg); intval = block_ioctl(dev, cmd, arg);
if (intval != -ENOTTY) if (intval != -ENOTTY)
...@@ -290,9 +299,10 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) ...@@ -290,9 +299,10 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
if (intval > PAGE_SIZE || intval < 512 || if (intval > PAGE_SIZE || intval < 512 ||
(intval & (intval - 1))) (intval & (intval - 1)))
return -EINVAL; return -EINVAL;
if (is_mounted(dev) || is_swap_partition(dev)) if (bd_claim(bdev, &holder) < 0)
return -EBUSY; return -EBUSY;
set_blocksize(dev, intval); set_blocksize(dev, intval);
bd_release(bdev);
return 0; return 0;
default: default:
......
...@@ -645,8 +645,14 @@ static int lock_rdev(mdk_rdev_t *rdev) ...@@ -645,8 +645,14 @@ static int lock_rdev(mdk_rdev_t *rdev)
if (!bdev) if (!bdev)
return -ENOMEM; return -ENOMEM;
err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_RAW); err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_RAW);
if (!err) if (err)
rdev->bdev = bdev; return err;
err = bd_claim(bdev, lock_rdev);
if (err) {
blkdev_put(bdev, BDEV_RAW);
return err;
}
rdev->bdev = bdev;
return err; return err;
} }
...@@ -656,6 +662,7 @@ static void unlock_rdev(mdk_rdev_t *rdev) ...@@ -656,6 +662,7 @@ static void unlock_rdev(mdk_rdev_t *rdev)
rdev->bdev = NULL; rdev->bdev = NULL;
if (!bdev) if (!bdev)
MD_BUG(); MD_BUG();
bd_release(bdev);
blkdev_put(bdev, BDEV_RAW); blkdev_put(bdev, BDEV_RAW);
} }
...@@ -1086,13 +1093,6 @@ static int md_import_device(kdev_t newdev, int on_disk) ...@@ -1086,13 +1093,6 @@ static int md_import_device(kdev_t newdev, int on_disk)
} }
memset(rdev, 0, sizeof(*rdev)); memset(rdev, 0, sizeof(*rdev));
if (is_mounted(newdev)) {
printk(KERN_WARNING "md: can not import %s, has active inodes!\n",
partition_name(newdev));
err = -EBUSY;
goto abort_free;
}
if ((err = alloc_disk_sb(rdev))) if ((err = alloc_disk_sb(rdev)))
goto abort_free; goto abort_free;
......
...@@ -442,6 +442,27 @@ void bd_forget(struct inode *inode) ...@@ -442,6 +442,27 @@ void bd_forget(struct inode *inode)
spin_unlock(&bdev_lock); spin_unlock(&bdev_lock);
} }
int bd_claim(struct block_device *bdev, void *holder)
{
int res = -EBUSY;
spin_lock(&bdev_lock);
if (!bdev->bd_holder || bdev->bd_holder == holder) {
bdev->bd_holder = holder;
bdev->bd_holders++;
res = 0;
}
spin_unlock(&bdev_lock);
return res;
}
void bd_release(struct block_device *bdev)
{
spin_lock(&bdev_lock);
if (!--bdev->bd_holders)
bdev->bd_holder = NULL;
spin_unlock(&bdev_lock);
}
static struct { static struct {
const char *name; const char *name;
struct block_device_operations *bdops; struct block_device_operations *bdops;
......
...@@ -444,9 +444,10 @@ static void shutdown_super(struct super_block *sb) ...@@ -444,9 +444,10 @@ static void shutdown_super(struct super_block *sb)
if (fs->fs_flags & FS_LITTER && sb->s_root) if (fs->fs_flags & FS_LITTER && sb->s_root)
d_genocide(sb->s_root); d_genocide(sb->s_root);
generic_shutdown_super(sb); generic_shutdown_super(sb);
if (bdev) if (bdev) {
bd_release(bdev);
blkdev_put(bdev, BDEV_FS); blkdev_put(bdev, BDEV_FS);
else } else
put_anon_dev(dev); put_anon_dev(dev);
} }
...@@ -727,37 +728,40 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type, ...@@ -727,37 +728,40 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
error = -EACCES; error = -EACCES;
if (!(flags & MS_RDONLY) && is_read_only(dev)) if (!(flags & MS_RDONLY) && is_read_only(dev))
goto out1; goto out1;
error = bd_claim(bdev, fs_type);
if (error)
goto out1;
error = -ENOMEM; error = -ENOMEM;
s = alloc_super(); s = alloc_super();
if (!s) if (!s)
goto out1; goto out2;
error = -EBUSY; error = -EBUSY;
restart: restart:
spin_lock(&sb_lock); spin_lock(&sb_lock);
list_for_each(p, &super_blocks) { list_for_each(p, &fs_type->fs_supers) {
struct super_block *old = sb_entry(p); struct super_block *old = sb_entry(p);
if (old->s_bdev != bdev) if (old->s_bdev != bdev)
continue; continue;
if (old->s_type != fs_type ||
((flags ^ old->s_flags) & MS_RDONLY)) {
spin_unlock(&sb_lock);
destroy_super(s);
goto out1;
}
if (!grab_super(old)) if (!grab_super(old))
goto restart; goto restart;
destroy_super(s); destroy_super(s);
if ((flags ^ old->s_flags) & MS_RDONLY) {
up_write(&old->s_umount);
kill_super(old);
old = ERR_PTR(-EBUSY);
}
bd_release(bdev);
blkdev_put(bdev, BDEV_FS); blkdev_put(bdev, BDEV_FS);
path_release(&nd); path_release(&nd);
return old; return old;
} }
s->s_dev = dev;
s->s_bdev = bdev; s->s_bdev = bdev;
s->s_flags = flags; s->s_dev = dev;
insert_super(s, fs_type); insert_super(s, fs_type);
s->s_flags = flags;
strncpy(s->s_id, bdevname(dev), sizeof(s->s_id)); strncpy(s->s_id, bdevname(dev), sizeof(s->s_id));
error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) if (error)
...@@ -770,6 +774,8 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type, ...@@ -770,6 +774,8 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
up_write(&s->s_umount); up_write(&s->s_umount);
kill_super(s); kill_super(s);
goto out; goto out;
out2:
bd_release(bdev);
out1: out1:
blkdev_put(bdev, BDEV_FS); blkdev_put(bdev, BDEV_FS);
out: out:
......
...@@ -400,6 +400,8 @@ struct block_device { ...@@ -400,6 +400,8 @@ struct block_device {
const struct block_device_operations *bd_op; const struct block_device_operations *bd_op;
struct semaphore bd_sem; /* open/close mutex */ struct semaphore bd_sem; /* open/close mutex */
struct list_head bd_inodes; struct list_head bd_inodes;
void * bd_holder;
int bd_holders;
}; };
struct inode { struct inode {
...@@ -1069,6 +1071,8 @@ extern struct file_operations def_fifo_fops; ...@@ -1069,6 +1071,8 @@ extern struct file_operations def_fifo_fops;
extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long); extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long);
extern int blkdev_get(struct block_device *, mode_t, unsigned, int); extern int blkdev_get(struct block_device *, mode_t, unsigned, int);
extern int blkdev_put(struct block_device *, int); extern int blkdev_put(struct block_device *, int);
extern int bd_claim(struct block_device *, void *);
extern void bd_release(struct block_device *);
/* fs/devices.c */ /* fs/devices.c */
extern const struct block_device_operations *get_blkfops(unsigned int); extern const struct block_device_operations *get_blkfops(unsigned int);
...@@ -1480,15 +1484,6 @@ extern int vfs_fstat(unsigned int, struct kstat *); ...@@ -1480,15 +1484,6 @@ extern int vfs_fstat(unsigned int, struct kstat *);
extern struct file_system_type *get_fs_type(const char *name); extern struct file_system_type *get_fs_type(const char *name);
extern struct super_block *get_super(kdev_t); extern struct super_block *get_super(kdev_t);
extern void drop_super(struct super_block *sb); extern void drop_super(struct super_block *sb);
static inline int is_mounted(kdev_t dev)
{
struct super_block *sb = get_super(dev);
if (sb) {
drop_super(sb);
return 1;
}
return 0;
}
extern kdev_t ROOT_DEV; extern kdev_t ROOT_DEV;
extern char root_device_name[]; extern char root_device_name[];
......
...@@ -151,7 +151,6 @@ extern void out_of_memory(void); ...@@ -151,7 +151,6 @@ extern void out_of_memory(void);
extern int total_swap_pages; extern int total_swap_pages;
extern unsigned int nr_swapfiles; extern unsigned int nr_swapfiles;
extern struct swap_info_struct swap_info[]; extern struct swap_info_struct swap_info[];
extern int is_swap_partition(kdev_t);
extern void si_swapinfo(struct sysinfo *); extern void si_swapinfo(struct sysinfo *);
extern swp_entry_t get_swap_page(void); extern swp_entry_t get_swap_page(void);
extern void get_swaphandle_info(swp_entry_t, unsigned long *, struct inode **); extern void get_swaphandle_info(swp_entry_t, unsigned long *, struct inode **);
......
...@@ -198,6 +198,8 @@ EXPORT_SYMBOL(cdget); ...@@ -198,6 +198,8 @@ EXPORT_SYMBOL(cdget);
EXPORT_SYMBOL(cdput); EXPORT_SYMBOL(cdput);
EXPORT_SYMBOL(bdget); EXPORT_SYMBOL(bdget);
EXPORT_SYMBOL(bdput); EXPORT_SYMBOL(bdput);
EXPORT_SYMBOL(bd_claim);
EXPORT_SYMBOL(bd_release);
EXPORT_SYMBOL(__bread); EXPORT_SYMBOL(__bread);
EXPORT_SYMBOL(__brelse); EXPORT_SYMBOL(__brelse);
EXPORT_SYMBOL(__bforget); EXPORT_SYMBOL(__bforget);
......
...@@ -786,6 +786,8 @@ asmlinkage long sys_swapoff(const char * specialfile) ...@@ -786,6 +786,8 @@ asmlinkage long sys_swapoff(const char * specialfile)
swap_device_unlock(p); swap_device_unlock(p);
swap_list_unlock(); swap_list_unlock();
vfree(swap_map); vfree(swap_map);
if (S_ISBLK(swap_file->f_dentry->d_inode->i_mode))
bd_release(swap_file->f_dentry->d_inode->i_bdev);
filp_close(swap_file, NULL); filp_close(swap_file, NULL);
err = 0; err = 0;
...@@ -833,19 +835,6 @@ int get_swaparea_info(char *buf) ...@@ -833,19 +835,6 @@ int get_swaparea_info(char *buf)
return len; return len;
} }
int is_swap_partition(kdev_t dev) {
struct swap_info_struct *ptr = swap_info;
int i;
for (i = 0 ; i < nr_swapfiles ; i++, ptr++) {
if ((ptr->flags & SWP_USED) &&
(ptr->flags & SWP_BLOCKDEV) &&
(kdev_same(ptr->swap_file->f_dentry->d_inode->i_rdev, dev)))
return 1;
}
return 0;
}
/* /*
* Written 01/25/92 by Simmule Turner, heavily changed by Linus. * Written 01/25/92 by Simmule Turner, heavily changed by Linus.
* *
...@@ -855,6 +844,7 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) ...@@ -855,6 +844,7 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
{ {
struct swap_info_struct * p; struct swap_info_struct * p;
char *name; char *name;
struct block_device *bdev = NULL;
struct file *swap_file = NULL; struct file *swap_file = NULL;
struct address_space *mapping; struct address_space *mapping;
unsigned int type; unsigned int type;
...@@ -905,13 +895,21 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) ...@@ -905,13 +895,21 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
swap_file = filp_open(name, O_RDWR, 0); swap_file = filp_open(name, O_RDWR, 0);
putname(name); putname(name);
error = PTR_ERR(swap_file); error = PTR_ERR(swap_file);
if (IS_ERR(swap_file)) if (IS_ERR(swap_file)) {
swap_file = NULL;
goto bad_swap_2; goto bad_swap_2;
}
p->swap_file = swap_file; p->swap_file = swap_file;
error = -EINVAL; error = -EINVAL;
if (S_ISBLK(swap_file->f_dentry->d_inode->i_mode)) { if (S_ISBLK(swap_file->f_dentry->d_inode->i_mode)) {
bdev = swap_file->f_dentry->d_inode->i_bdev;
error = bd_claim(bdev, sys_swapon);
if (error < 0) {
bdev = NULL;
goto bad_swap;
}
error = set_blocksize(swap_file->f_dentry->d_inode->i_rdev, error = set_blocksize(swap_file->f_dentry->d_inode->i_rdev,
PAGE_SIZE); PAGE_SIZE);
if (error < 0) if (error < 0)
...@@ -1066,6 +1064,8 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) ...@@ -1066,6 +1064,8 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
error = 0; error = 0;
goto out; goto out;
bad_swap: bad_swap:
if (bdev)
bd_release(bdev);
bad_swap_2: bad_swap_2:
swap_list_lock(); swap_list_lock();
swap_map = p->swap_map; swap_map = p->swap_map;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment