diff options
| author | Alexander Viro <viro@math.psu.edu> | 2002-04-24 23:50:28 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-04-24 23:50:28 -0700 |
| commit | 79c2cfb85fd83474e12f95ad85624eb421f4b6ea (patch) | |
| tree | 8f7efa3eb8ed7f2da825ac08c60c3951f757a8ab | |
| parent | eec08e21be012356dc94b0266419696f53b8d5d8 (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.c | 74 | ||||
| -rw-r--r-- | fs/partitions/check.c | 1 | ||||
| -rw-r--r-- | include/linux/fs.h | 1 |
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 { |
