[CFT][PATCH] (2.4.10-pre10) lazy allocation of struct block_device

Alexander Viro (viro@math.psu.edu)
Mon, 17 Sep 2001 13:23:41 -0400 (EDT)


Ported to 2.4.10-pre10, fixed a braino in md.c. Please, help
with testing.
Al

diff -urN S10-pre10/drivers/block/rd.c S10-pre10-bdev/drivers/block/rd.c
--- S10-pre10/drivers/block/rd.c Sun Sep 16 18:11:37 2001
+++ S10-pre10-bdev/drivers/block/rd.c Mon Sep 17 13:16:33 2001
@@ -374,10 +374,8 @@
for (i = 0 ; i < NUM_RAMDISKS; i++) {
struct block_device *bdev = rd_bdev[i];
rd_bdev[i] = NULL;
- if (bdev) {
+ if (bdev)
blkdev_put(bdev, BDEV_FILE);
- bdput(bdev);
- }
destroy_buffers(MKDEV(MAJOR_NR, i));
}

diff -urN S10-pre10/drivers/char/raw.c S10-pre10-bdev/drivers/char/raw.c
--- S10-pre10/drivers/char/raw.c Tue Jul 3 21:09:09 2001
+++ S10-pre10-bdev/drivers/char/raw.c Mon Sep 17 13:16:33 2001
@@ -100,6 +100,7 @@
goto out;

rdev = to_kdev_t(bdev->bd_dev);
+ atomic_inc(&bdev->bd_count);
err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW);
if (err)
goto out;
diff -urN S10-pre10/drivers/md/md.c S10-pre10-bdev/drivers/md/md.c
--- S10-pre10/drivers/md/md.c Sun Sep 16 18:11:44 2001
+++ S10-pre10-bdev/drivers/md/md.c Mon Sep 17 13:19:28 2001
@@ -649,11 +649,11 @@

static void unlock_rdev (mdk_rdev_t *rdev)
{
- if (!rdev->bdev)
- MD_BUG();
- blkdev_put(rdev->bdev, BDEV_RAW);
- bdput(rdev->bdev);
+ struct block_device *bdev = rdev->bdev;
rdev->bdev = NULL;
+ if (!bdev)
+ MD_BUG();
+ blkdev_put(bdev, BDEV_RAW);
}

void md_autodetect_dev (kdev_t dev);
diff -urN S10-pre10/fs/block_dev.c S10-pre10-bdev/fs/block_dev.c
--- S10-pre10/fs/block_dev.c Sun Sep 16 18:11:52 2001
+++ S10-pre10-bdev/fs/block_dev.c Mon Sep 17 13:16:33 2001
@@ -389,6 +389,7 @@
{
memset(bdev, 0, sizeof(*bdev));
sema_init(&bdev->bd_sem, 1);
+ INIT_LIST_HEAD(&bdev->bd_inodes);
}
}

@@ -466,16 +467,57 @@

void bdput(struct block_device *bdev)
{
- if (atomic_dec_and_test(&bdev->bd_count)) {
- spin_lock(&bdev_lock);
+ if (atomic_dec_and_lock(&bdev->bd_count, &bdev_lock)) {
+ struct list_head *p;
if (atomic_read(&bdev->bd_openers))
BUG();
list_del(&bdev->bd_hash);
+ while ( (p = bdev->bd_inodes.next) != &bdev->bd_inodes ) {
+ struct inode *inode;
+ inode = list_entry(p, struct inode, i_devices);
+ list_del(p);
+ inode->i_bdev = NULL;
+ }
spin_unlock(&bdev_lock);
destroy_bdev(bdev);
}
}

+int bd_acquire(struct inode *inode)
+{
+ struct block_device *bdev;
+ spin_lock(&bdev_lock);
+ if (inode->i_bdev) {
+ atomic_inc(&inode->i_bdev->bd_count);
+ spin_unlock(&bdev_lock);
+ return 0;
+ }
+ spin_unlock(&bdev_lock);
+ bdev = bdget(kdev_t_to_nr(inode->i_rdev));
+ if (!bdev)
+ return -ENOMEM;
+ spin_lock(&bdev_lock);
+ if (!inode->i_bdev) {
+ inode->i_bdev = bdev;
+ list_add(&inode->i_devices, &bdev->bd_inodes);
+ } else if (inode->i_bdev != bdev)
+ BUG();
+ spin_unlock(&bdev_lock);
+ return 0;
+}
+
+/* Call when you free inode */
+
+void bd_forget(struct inode *inode)
+{
+ spin_lock(&bdev_lock);
+ if (inode->i_bdev) {
+ list_del(&inode->i_devices);
+ inode->i_bdev = NULL;
+ }
+ spin_unlock(&bdev_lock);
+}
+
static struct {
const char *name;
struct block_device_operations *bdops;
@@ -646,13 +688,17 @@
}
}
up(&bdev->bd_sem);
+ if (ret)
+ bdput(bdev);
return ret;
}

int blkdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENXIO;
- struct block_device *bdev = inode->i_bdev;
+ struct block_device *bdev;
+ bd_acquire(inode);
+ bdev = inode->i_bdev;
down(&bdev->bd_sem);
lock_kernel();
if (!bdev->bd_op)
@@ -668,6 +714,8 @@
}
unlock_kernel();
up(&bdev->bd_sem);
+ if (ret)
+ bdput(bdev);
return ret;
}

@@ -700,6 +748,7 @@
/* kind to stay around. */
unlock_kernel();
up(&bdev->bd_sem);
+ bdput(bdev);
return ret;
}

diff -urN S10-pre10/fs/devfs/base.c S10-pre10-bdev/fs/devfs/base.c
--- S10-pre10/fs/devfs/base.c Sun Sep 16 18:11:52 2001
+++ S10-pre10-bdev/fs/devfs/base.c Mon Sep 17 13:16:33 2001
@@ -2247,6 +2247,12 @@
static struct file_operations devfs_dir_fops;
static struct inode_operations devfs_symlink_iops;

+static void devfs_clear_inode (struct inode *inode)
+{
+ if (S_ISBLK(inode->i_mode))
+ bdput(inode->i_bdev);
+}
+
static void devfs_read_inode (struct inode *inode)
{
struct devfs_entry *de;
@@ -2278,8 +2284,7 @@
{
inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
de->u.fcb.u.device.minor);
- inode->i_bdev = bdget (kdev_t_to_nr(inode->i_rdev));
- if (inode->i_bdev)
+ if (bd_acquire(inode) == 0)
{
if (!inode->i_bdev->bd_op && de->u.fcb.ops)
inode->i_bdev->bd_op = de->u.fcb.ops;
@@ -2384,6 +2389,7 @@
{
read_inode: devfs_read_inode,
write_inode: devfs_write_inode,
+ clear_inode: devfs_clear_inode,
statfs: devfs_statfs,
};

diff -urN S10-pre10/fs/devices.c S10-pre10-bdev/fs/devices.c
--- S10-pre10/fs/devices.c Thu May 24 18:26:44 2001
+++ S10-pre10-bdev/fs/devices.c Mon Sep 17 13:16:33 2001
@@ -207,7 +207,6 @@
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = to_kdev_t(rdev);
- inode->i_bdev = bdget(rdev);
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
diff -urN S10-pre10/fs/inode.c S10-pre10-bdev/fs/inode.c
--- S10-pre10/fs/inode.c Sun Sep 16 18:11:52 2001
+++ S10-pre10-bdev/fs/inode.c Mon Sep 17 13:16:33 2001
@@ -516,11 +516,9 @@
DQUOT_DROP(inode);
if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)
inode->i_sb->s_op->clear_inode(inode);
- if (inode->i_bdev) {
- bdput(inode->i_bdev);
- inode->i_bdev = NULL;
- }
- if (inode->i_cdev) {
+ if (inode->i_bdev)
+ bd_forget(inode);
+ else if (inode->i_cdev) {
cdput(inode->i_cdev);
inode->i_cdev = NULL;
}
@@ -778,6 +776,7 @@
inode->i_size = 0;
inode->i_generation = 0;
memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
+ INIT_LIST_HEAD(&inode->i_devices);
inode->i_pipe = NULL;
inode->i_bdev = NULL;
inode->i_cdev = NULL;
diff -urN S10-pre10/fs/partitions/ibm.c S10-pre10-bdev/fs/partitions/ibm.c
--- S10-pre10/fs/partitions/ibm.c Sun Sep 16 18:11:53 2001
+++ S10-pre10-bdev/fs/partitions/ibm.c Mon Sep 17 13:16:33 2001
@@ -47,9 +47,10 @@
{
struct block_device *bdev = bdget(kdev_t_to_nr(kdev));
int rc = blkdev_get(bdev, 0, 1, BDEV_FILE);
- if ( rc == 0 )
+ if ( rc == 0 ) {
rc = ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo);
- blkdev_put(bdev,BDEV_FILE);
+ blkdev_put(bdev,BDEV_FILE);
+ }
return rc;
}

@@ -58,9 +59,10 @@
{
struct block_device *bdev = bdget(kdev_t_to_nr(kdev));
int rc = blkdev_get(bdev, 0, 1, BDEV_FILE);
- if ( rc == 0 )
+ if ( rc == 0 ) {
rc = ioctl_by_bdev(bdev, BIODASDINFO, (unsigned long)(info));
- blkdev_put(bdev,BDEV_FILE);
+ blkdev_put(bdev,BDEV_FILE);
+ }
return rc;
}

diff -urN S10-pre10/fs/super.c S10-pre10-bdev/fs/super.c
--- S10-pre10/fs/super.c Sun Sep 16 18:11:54 2001
+++ S10-pre10-bdev/fs/super.c Mon Sep 17 13:16:33 2001
@@ -925,6 +925,7 @@
error = -EACCES;
if (nd.mnt->mnt_flags & MNT_NODEV)
goto out;
+ bd_acquire(inode);
bdev = inode->i_bdev;
bdops = devfs_get_ops ( devfs_get_handle_from_inode (inode) );
if (bdops) bdev->bd_op = bdops;
@@ -982,8 +983,6 @@
if (!fs_type->read_super(s, data, 0))
goto out_fail;
unlock_super(s);
- /* tell bdcache that we are going to keep this one */
- atomic_inc(&bdev->bd_count);
get_filesystem(fs_type);
path_release(&nd);
return s;
@@ -1128,10 +1127,9 @@
sb->s_type = NULL;
unlock_super(sb);
unlock_kernel();
- if (bdev) {
+ if (bdev)
blkdev_put(bdev, BDEV_FS);
- bdput(bdev);
- } else
+ else
put_unnamed_dev(dev);
spin_lock(&sb_lock);
list_del(&sb->s_list);
@@ -1718,6 +1716,7 @@
if (!ROOT_DEV)
panic("I have no root and I want to scream");

+retry:
bdev = bdget(kdev_t_to_nr(ROOT_DEV));
if (!bdev)
panic(__FUNCTION__ ": unable to allocate root device");
@@ -1729,7 +1728,7 @@
retval = blkdev_get(bdev, mode, 0, BDEV_FS);
if (retval == -EROFS) {
root_mountflags |= MS_RDONLY;
- retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS);
+ goto retry;
}
if (retval) {
/*
@@ -1977,6 +1976,7 @@
int blivet;
struct block_device *ramdisk = old_rootmnt->mnt_sb->s_bdev;

+ atomic_inc(&ramdisk->bd_count);
blivet = blkdev_get(ramdisk, FMODE_READ, 0, BDEV_FS);
printk(KERN_NOTICE "Trying to unmount old root ... ");
if (!blivet) {
diff -urN S10-pre10/include/linux/fs.h S10-pre10-bdev/include/linux/fs.h
--- S10-pre10/include/linux/fs.h Sun Sep 16 18:11:55 2001
+++ S10-pre10-bdev/include/linux/fs.h Mon Sep 17 13:16:33 2001
@@ -406,6 +406,7 @@
atomic_t bd_openers;
const struct block_device_operations *bd_op;
struct semaphore bd_sem; /* open/close mutex */
+ struct list_head bd_inodes;
};

struct inode {
@@ -440,6 +441,7 @@
struct address_space *i_mapping;
struct address_space i_data;
struct dquot *i_dquot[MAXQUOTAS];
+ struct list_head i_devices;
/* These three should probably be a union */
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
@@ -1031,6 +1033,8 @@
extern int register_blkdev(unsigned int, const char *, struct block_device_operations *);
extern int unregister_blkdev(unsigned int, const char *);
extern struct block_device *bdget(dev_t);
+extern int bd_acquire(struct inode *inode);
+extern void bd_forget(struct inode *inode);
extern void bdput(struct block_device *);
extern struct char_device *cdget(dev_t);
extern void cdput(struct char_device *);
diff -urN S10-pre10/mm/swapfile.c S10-pre10-bdev/mm/swapfile.c
--- S10-pre10/mm/swapfile.c Sun Sep 16 18:11:56 2001
+++ S10-pre10-bdev/mm/swapfile.c Mon Sep 17 13:16:33 2001
@@ -764,6 +764,7 @@
p->swap_device = dev;
set_blocksize(dev, PAGE_SIZE);

+ bd_acquire(swap_inode);
bdev = swap_inode->i_bdev;
bdops = devfs_get_ops(devfs_get_handle_from_inode(swap_inode));
if (bdops) bdev->bd_op = bdops;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/