summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Viro <viro@math.psu.edu>2002-04-24 23:50:28 -0700
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-04-24 23:50:28 -0700
commit79c2cfb85fd83474e12f95ad85624eb421f4b6ea (patch)
tree8f7efa3eb8ed7f2da825ac08c60c3951f757a8ab
parenteec08e21be012356dc94b0266419696f53b8d5d8 (diff)
[PATCH] (9/15) big struct block_device * push (first series)
- this one is interesting and will play in the next series as well; affected place is fs/block_dev.c::do_open(). We check if bdev is a partition (same way it is done in generic_make_request()) and if it is - open entire disk and put pointer to its bdev into a new field bdev->bd_contains. Otherwise (non-partition) we set bdev->bd_contains to bdev. Corresponding cleanup done in blkdev_put() (and failure path in do_open()) - when the last opener goes away we close bdev->bd_contains if bdev is a partition (i.e. not equal to its ->bd_contains) and set it to NULL. Immediate effect is that we can get from bdev of partition to bdev of disk when submitting a bio, but it also opens a way to handle partition-parsing in a sane way. That will be done in the next series.
-rw-r--r--fs/block_dev.c74
-rw-r--r--fs/partitions/check.c1
-rw-r--r--include/linux/fs.h1
3 files changed, 59 insertions, 17 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index ba5842f6981d..f7460a3283b9 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -353,6 +353,7 @@ struct block_device *bdget(dev_t dev)
atomic_set(&new_bdev->bd_count,1);
new_bdev->bd_dev = dev;
new_bdev->bd_op = NULL;
+ new_bdev->bd_contains = NULL;
new_bdev->bd_inode = inode;
inode->i_mode = S_IFBLK;
inode->i_rdev = kdev;
@@ -590,28 +591,62 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
{
int ret = -ENXIO;
kdev_t dev = to_kdev_t(bdev->bd_dev);
+ struct module *owner = NULL;
down(&bdev->bd_sem);
lock_kernel();
- if (!bdev->bd_op)
+ if (!bdev->bd_op) {
bdev->bd_op = get_blkfops(major(dev));
- if (bdev->bd_op) {
- ret = 0;
- if (bdev->bd_op->owner)
- __MOD_INC_USE_COUNT(bdev->bd_op->owner);
- if (bdev->bd_op->open)
- ret = bdev->bd_op->open(inode, file);
- if (!ret) {
- bdev->bd_openers++;
- bdev->bd_inode->i_size = blkdev_size(dev);
- bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
- } else {
- if (bdev->bd_op->owner)
- __MOD_DEC_USE_COUNT(bdev->bd_op->owner);
- if (!bdev->bd_openers)
- bdev->bd_op = NULL;
+ if (!bdev->bd_op)
+ goto out;
+ owner = bdev->bd_op->owner;
+ if (owner)
+ __MOD_INC_USE_COUNT(owner);
+ }
+ if (!bdev->bd_contains) {
+ unsigned minor = minor(dev);
+ struct gendisk *g = get_gendisk(dev);
+ bdev->bd_contains = bdev;
+ if (g) {
+ int shift = g->minor_shift;
+ unsigned minor0 = (minor >> shift) << shift;
+ if (minor != minor0) {
+ struct block_device *disk;
+ disk = bdget(MKDEV(major(dev), minor0));
+ ret = -ENOMEM;
+ if (!disk)
+ goto out1;
+ ret = blkdev_get(disk, file->f_mode, file->f_flags, BDEV_RAW);
+ if (ret)
+ goto out1;
+ bdev->bd_contains = disk;
+ }
+ }
+ }
+ if (bdev->bd_op->open) {
+ ret = bdev->bd_op->open(inode, file);
+ if (ret)
+ goto out2;
+ }
+ bdev->bd_openers++;
+ bdev->bd_inode->i_size = blkdev_size(dev);
+ bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
+ unlock_kernel();
+ up(&bdev->bd_sem);
+ return 0;
+
+out2:
+ if (!bdev->bd_openers) {
+ bdev->bd_op = NULL;
+ if (bdev != bdev->bd_contains) {
+ blkdev_put(bdev->bd_contains, BDEV_RAW);
+ bdev->bd_contains = NULL;
}
}
+out1:
+ if (owner)
+ __MOD_DEC_USE_COUNT(owner);
+out:
unlock_kernel();
up(&bdev->bd_sem);
if (ret)
@@ -676,8 +711,13 @@ int blkdev_put(struct block_device *bdev, int kind)
ret = bdev->bd_op->release(bd_inode, NULL);
if (bdev->bd_op->owner)
__MOD_DEC_USE_COUNT(bdev->bd_op->owner);
- if (!bdev->bd_openers)
+ if (!bdev->bd_openers) {
bdev->bd_op = NULL;
+ if (bdev != bdev->bd_contains) {
+ blkdev_put(bdev->bd_contains, BDEV_RAW);
+ bdev->bd_contains = NULL;
+ }
+ }
unlock_kernel();
up(&bdev->bd_sem);
bdput(bdev);
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index e7e8579dfbbd..003f996f6a2f 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -251,6 +251,7 @@ static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor
else
printk(KERN_INFO " %s:", disk_name(hd, minor(dev), buf));
bdev = bdget(kdev_t_to_nr(dev));
+ bdev->bd_contains = bdev;
bdev->bd_inode->i_size = (loff_t)hd->part[minor(dev)].nr_sects << 9;
bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
for (i = 0; check_part[i]; i++) {
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a97a764dcc53..7a05fbe4b64c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -420,6 +420,7 @@ struct block_device {
struct list_head bd_inodes;
void * bd_holder;
int bd_holders;
+ struct block_device * bd_contains;
};
struct inode {