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 @@
#include <linux/blk.h> /* for set_device_ro() */
#include <linux/blkpg.h>
#include <linux/genhd.h>
#include <linux/swap.h> /* for is_swap_partition() */
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <asm/uaccess.h>
......@@ -132,7 +131,9 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p)
kdev_t dev = to_kdev_t(bdev->bd_dev);
struct gendisk *g;
kdev_t devp;
struct block_device *bdevp;
int drive, first_minor, minor;
int holder;
/* find the drive major */
g = get_gendisk(dev);
......@@ -155,17 +156,24 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p)
/* partition in use? Incomplete check for now. */
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;
}
/* all seems OK */
fsync_dev(devp);
invalidate_buffers(devp);
fsync_bdev(bdevp);
invalidate_bdev(bdevp, 0);
g->part[minor].start_sect = 0;
g->part[minor].nr_sects = 0;
if (g->sizes)
g->sizes[minor] = 0;
bd_release(bdevp);
bdput(bdevp);
return 0;
}
......@@ -210,6 +218,7 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
int intval;
unsigned short usval;
kdev_t dev = to_kdev_t(bdev->bd_dev);
int holder;
intval = block_ioctl(dev, cmd, arg);
if (intval != -ENOTTY)
......@@ -290,9 +299,10 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
if (intval > PAGE_SIZE || intval < 512 ||
(intval & (intval - 1)))
return -EINVAL;
if (is_mounted(dev) || is_swap_partition(dev))
if (bd_claim(bdev, &holder) < 0)
return -EBUSY;
set_blocksize(dev, intval);
bd_release(bdev);
return 0;
default:
......
......@@ -645,8 +645,14 @@ static int lock_rdev(mdk_rdev_t *rdev)
if (!bdev)
return -ENOMEM;
err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_RAW);
if (!err)
rdev->bdev = bdev;
if (err)
return err;
err = bd_claim(bdev, lock_rdev);
if (err) {
blkdev_put(bdev, BDEV_RAW);
return err;
}
rdev->bdev = bdev;
return err;
}
......@@ -656,6 +662,7 @@ static void unlock_rdev(mdk_rdev_t *rdev)
rdev->bdev = NULL;
if (!bdev)
MD_BUG();
bd_release(bdev);
blkdev_put(bdev, BDEV_RAW);
}
......@@ -1086,13 +1093,6 @@ static int md_import_device(kdev_t newdev, int on_disk)
}
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)))
goto abort_free;
......
......@@ -442,6 +442,27 @@ void bd_forget(struct inode *inode)
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 {
const char *name;
struct block_device_operations *bdops;
......
......@@ -444,9 +444,10 @@ static void shutdown_super(struct super_block *sb)
if (fs->fs_flags & FS_LITTER && sb->s_root)
d_genocide(sb->s_root);
generic_shutdown_super(sb);
if (bdev)
if (bdev) {
bd_release(bdev);
blkdev_put(bdev, BDEV_FS);
else
} else
put_anon_dev(dev);
}
......@@ -727,37 +728,40 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
error = -EACCES;
if (!(flags & MS_RDONLY) && is_read_only(dev))
goto out1;
error = bd_claim(bdev, fs_type);
if (error)
goto out1;
error = -ENOMEM;
s = alloc_super();
if (!s)
goto out1;
goto out2;
error = -EBUSY;
restart:
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);
if (old->s_bdev != bdev)
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))
goto restart;
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);
path_release(&nd);
return old;
}
s->s_dev = dev;
s->s_bdev = bdev;
s->s_flags = flags;
s->s_dev = dev;
insert_super(s, fs_type);
s->s_flags = flags;
strncpy(s->s_id, bdevname(dev), sizeof(s->s_id));
error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error)
......@@ -770,6 +774,8 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
up_write(&s->s_umount);
kill_super(s);
goto out;
out2:
bd_release(bdev);
out1:
blkdev_put(bdev, BDEV_FS);
out:
......
......@@ -400,6 +400,8 @@ struct block_device {
const struct block_device_operations *bd_op;
struct semaphore bd_sem; /* open/close mutex */
struct list_head bd_inodes;
void * bd_holder;
int bd_holders;
};
struct inode {
......@@ -1069,6 +1071,8 @@ extern struct file_operations def_fifo_fops;
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_put(struct block_device *, int);
extern int bd_claim(struct block_device *, void *);
extern void bd_release(struct block_device *);
/* fs/devices.c */
extern const struct block_device_operations *get_blkfops(unsigned int);
......@@ -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 super_block *get_super(kdev_t);
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 char root_device_name[];
......
......@@ -151,7 +151,6 @@ extern void out_of_memory(void);
extern int total_swap_pages;
extern unsigned int nr_swapfiles;
extern struct swap_info_struct swap_info[];
extern int is_swap_partition(kdev_t);
extern void si_swapinfo(struct sysinfo *);
extern swp_entry_t get_swap_page(void);
extern void get_swaphandle_info(swp_entry_t, unsigned long *, struct inode **);
......
......@@ -198,6 +198,8 @@ EXPORT_SYMBOL(cdget);
EXPORT_SYMBOL(cdput);
EXPORT_SYMBOL(bdget);
EXPORT_SYMBOL(bdput);
EXPORT_SYMBOL(bd_claim);
EXPORT_SYMBOL(bd_release);
EXPORT_SYMBOL(__bread);
EXPORT_SYMBOL(__brelse);
EXPORT_SYMBOL(__bforget);
......
......@@ -786,6 +786,8 @@ asmlinkage long sys_swapoff(const char * specialfile)
swap_device_unlock(p);
swap_list_unlock();
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);
err = 0;
......@@ -833,19 +835,6 @@ int get_swaparea_info(char *buf)
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.
*
......@@ -855,6 +844,7 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
{
struct swap_info_struct * p;
char *name;
struct block_device *bdev = NULL;
struct file *swap_file = NULL;
struct address_space *mapping;
unsigned int type;
......@@ -905,13 +895,21 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
swap_file = filp_open(name, O_RDWR, 0);
putname(name);
error = PTR_ERR(swap_file);
if (IS_ERR(swap_file))
if (IS_ERR(swap_file)) {
swap_file = NULL;
goto bad_swap_2;
}
p->swap_file = swap_file;
error = -EINVAL;
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,
PAGE_SIZE);
if (error < 0)
......@@ -1066,6 +1064,8 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
error = 0;
goto out;
bad_swap:
if (bdev)
bd_release(bdev);
bad_swap_2:
swap_list_lock();
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