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)
 {

_