From: viro@www.linux.org.uk added the exclusion between ADD_PARTITION/DELETE_PARTITION/open() (BLKPG ioctls didn't grab ->bd_sem when they should have). added bdev->bd_part; it is set at open() to point to the hd_struct of partition in question, reset on final close. blk_partition_remap() uses ->bd_part instead of the current mess ->bd_offset is gone, we use ->bd_part->start_sect instead added missing ->release() to hd_struct kobject, moved kfree() into it ->bd_part contributes to refcount of hd_struct - we bump it when ->bd_part is set and drop when it's reset. drivers/block/genhd.c | 24 ++++++++---------------- drivers/block/ioctl.c | 21 +++++++++++++++------ drivers/block/ll_rw_blk.c | 31 +++++++++++++++---------------- fs/block_dev.c | 8 ++++++-- fs/partitions/check.c | 12 +++++++++--- include/linux/fs.h | 2 +- include/linux/genhd.h | 2 +- 7 files changed, 55 insertions(+), 45 deletions(-) diff -puN drivers/block/genhd.c~large-dev_t-12 drivers/block/genhd.c --- 25/drivers/block/genhd.c~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/drivers/block/genhd.c 2003-08-27 10:31:45.000000000 -0700 @@ -576,13 +576,10 @@ EXPORT_SYMBOL(put_disk); void set_device_ro(struct block_device *bdev, int flag) { - struct gendisk *disk = bdev->bd_disk; - if (bdev->bd_contains != bdev) { - int part = bdev->bd_dev - MKDEV(disk->major, disk->first_minor); - struct hd_struct *p = disk->part[part-1]; - if (p) p->policy = flag; - } else - disk->policy = flag; + if (bdev->bd_contains != bdev) + bdev->bd_part->policy = flag; + else + bdev->bd_disk->policy = flag; } void set_disk_ro(struct gendisk *disk, int flag) @@ -595,17 +592,12 @@ void set_disk_ro(struct gendisk *disk, i int bdev_read_only(struct block_device *bdev) { - struct gendisk *disk; if (!bdev) return 0; - disk = bdev->bd_disk; - if (bdev->bd_contains != bdev) { - int part = bdev->bd_dev - MKDEV(disk->major, disk->first_minor); - struct hd_struct *p = disk->part[part-1]; - if (p) return p->policy; - return 0; - } else - return disk->policy; + else if (bdev->bd_contains != bdev) + return bdev->bd_part->policy; + else + return bdev->bd_disk->policy; } int invalidate_partition(struct gendisk *disk, int index) diff -puN drivers/block/ioctl.c~large-dev_t-12 drivers/block/ioctl.c --- 25/drivers/block/ioctl.c~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/drivers/block/ioctl.c 2003-08-27 10:31:45.000000000 -0700 @@ -8,7 +8,6 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) { struct block_device *bdevp; - int holder; struct gendisk *disk; struct blkpg_ioctl_arg a; struct blkpg_partition p; @@ -41,8 +40,11 @@ static int blkpg_ioctl(struct block_devi return -EINVAL; } /* partition number in use? */ - if (disk->part[part - 1]) + down(&bdev->bd_sem); + if (disk->part[part - 1]) { + up(&bdev->bd_sem); return -EBUSY; + } /* overlap? */ for (i = 0; i < disk->minors - 1; i++) { struct hd_struct *s = disk->part[i]; @@ -50,22 +52,26 @@ static int blkpg_ioctl(struct block_devi if (!s) continue; if (!(start+length <= s->start_sect || - start >= s->start_sect + s->nr_sects)) + start >= s->start_sect + s->nr_sects)) { + up(&bdev->bd_sem); return -EBUSY; + } } /* all seems OK */ add_partition(disk, part, start, length); + up(&bdev->bd_sem); return 0; case BLKPG_DEL_PARTITION: if (!disk->part[part-1]) return -ENXIO; if (disk->part[part - 1]->nr_sects == 0) return -ENXIO; - /* partition in use? Incomplete check for now. */ bdevp = bdget_disk(disk, part); if (!bdevp) return -ENOMEM; - if (bd_claim(bdevp, &holder) < 0) { + down(&bdevp->bd_sem); + if (bdevp->bd_openers) { + up(&bdevp->bd_sem); bdput(bdevp); return -EBUSY; } @@ -73,9 +79,12 @@ static int blkpg_ioctl(struct block_devi fsync_bdev(bdevp); invalidate_bdev(bdevp, 0); + down(&bdev->bd_sem); delete_partition(disk, part); - bd_release(bdevp); + up(&bdev->bd_sem); + up(&bdevp->bd_sem); bdput(bdevp); + return 0; default: return -EINVAL; diff -puN drivers/block/ll_rw_blk.c~large-dev_t-12 drivers/block/ll_rw_blk.c --- 25/drivers/block/ll_rw_blk.c~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/drivers/block/ll_rw_blk.c 2003-08-27 10:31:45.000000000 -0700 @@ -2043,24 +2043,23 @@ end_io: static inline void blk_partition_remap(struct bio *bio) { struct block_device *bdev = bio->bi_bdev; - struct gendisk *disk = bdev->bd_disk; - struct hd_struct *p; - if (bdev == bdev->bd_contains) - return; - p = disk->part[bdev->bd_dev-MKDEV(disk->major,disk->first_minor)-1]; - switch (bio->bi_rw) { - case READ: - p->read_sectors += bio_sectors(bio); - p->reads++; - break; - case WRITE: - p->write_sectors += bio_sectors(bio); - p->writes++; - break; + if (bdev != bdev->bd_contains) { + struct hd_struct *p = bdev->bd_part; + + switch (bio->bi_rw) { + case READ: + p->read_sectors += bio_sectors(bio); + p->reads++; + break; + case WRITE: + p->write_sectors += bio_sectors(bio); + p->writes++; + break; + } + bio->bi_sector += p->start_sect; + bio->bi_bdev = bdev->bd_contains; } - bio->bi_sector += bdev->bd_offset; - bio->bi_bdev = bdev->bd_contains; } /** diff -puN fs/block_dev.c~large-dev_t-12 fs/block_dev.c --- 25/fs/block_dev.c~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/fs/block_dev.c 2003-08-27 10:31:45.000000000 -0700 @@ -540,7 +540,6 @@ static int do_open(struct block_device * if (ret) goto out_first; } - bdev->bd_offset = 0; if (!bdev->bd_openers) { bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); bdi = blk_get_backing_dev_info(bdev); @@ -572,7 +571,8 @@ static int do_open(struct block_device * ret = -ENXIO; goto out_first; } - bdev->bd_offset = p->start_sect; + kobject_get(&p->kobj); + bdev->bd_part = p; bd_set_size(bdev, (loff_t) p->nr_sects << 9); up(&whole->bd_sem); } @@ -693,6 +693,10 @@ int blkdev_put(struct block_device *bdev put_disk(disk); module_put(owner); + if (bdev->bd_contains != bdev) { + kobject_put(&bdev->bd_part->kobj); + bdev->bd_part = NULL; + } bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) { diff -puN fs/partitions/check.c~large-dev_t-12 fs/partitions/check.c --- 25/fs/partitions/check.c~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/fs/partitions/check.c 2003-08-27 10:31:45.000000000 -0700 @@ -267,7 +267,14 @@ static struct attribute * default_attrs[ extern struct subsystem block_subsys; +static void part_release(struct kobject *kobj) +{ + struct hd_struct * p = container_of(kobj,struct hd_struct,kobj); + kfree(p); +} + struct kobj_type ktype_part = { + .release = part_release, .default_attrs = default_attrs, .sysfs_ops = &part_sysfs_ops, }; @@ -279,13 +286,12 @@ void delete_partition(struct gendisk *di return; if (!p->nr_sects) return; + disk->part[part-1] = NULL; p->start_sect = 0; p->nr_sects = 0; p->reads = p->writes = p->read_sectors = p->write_sectors = 0; devfs_remove("%s/part%d", disk->devfs_name, part); kobject_unregister(&p->kobj); - disk->part[part-1] = NULL; - kfree(p); } void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) @@ -300,7 +306,6 @@ void add_partition(struct gendisk *disk, p->start_sect = start; p->nr_sects = len; p->partno = part; - disk->part[part-1] = p; devfs_mk_bdev(MKDEV(disk->major, disk->first_minor + part), S_IFBLK|S_IRUSR|S_IWUSR, @@ -310,6 +315,7 @@ void add_partition(struct gendisk *disk, p->kobj.parent = &disk->kobj; p->kobj.ktype = &ktype_part; kobject_register(&p->kobj); + disk->part[part-1] = p; } static void disk_sysfs_symlinks(struct gendisk *disk) diff -puN include/linux/fs.h~large-dev_t-12 include/linux/fs.h --- 25/include/linux/fs.h~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/include/linux/fs.h 2003-08-27 10:31:45.000000000 -0700 @@ -345,7 +345,7 @@ struct block_device { int bd_holders; struct block_device * bd_contains; unsigned bd_block_size; - sector_t bd_offset; + struct hd_struct * bd_part; unsigned bd_part_count; int bd_invalidated; struct gendisk * bd_disk; diff -puN include/linux/genhd.h~large-dev_t-12 include/linux/genhd.h --- 25/include/linux/genhd.h~large-dev_t-12 2003-08-27 10:31:45.000000000 -0700 +++ 25-akpm/include/linux/genhd.h 2003-08-27 10:31:45.000000000 -0700 @@ -197,7 +197,7 @@ extern void rand_initialize_disk(struct static inline sector_t get_start_sect(struct block_device *bdev) { - return bdev->bd_offset; + return bdev->bd_part->start_sect; } static inline sector_t get_capacity(struct gendisk *disk) { _