Commit 68c16870 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] refcounts for gendisks

Finally.  We use disk->dev.refcount as a gendisk refcount.  New helper -
get_disk(): atomic_inc on refcount.  get_gendisk() does it on return,
callers of get_gendisk() do put_disk() when they are done.
parent b288f6ad
......@@ -100,6 +100,8 @@ get_gendisk(dev_t dev, int *part)
read_lock(&gendisk_lock);
if (gendisks[major].get) {
disk = gendisks[major].get(minor);
if (disk)
get_disk(disk);
read_unlock(&gendisk_lock);
return disk;
}
......@@ -109,6 +111,7 @@ get_gendisk(dev_t dev, int *part)
continue;
if (disk->first_minor + disk->minors <= minor)
continue;
get_disk(disk);
read_unlock(&gendisk_lock);
*part = minor - disk->first_minor;
return disk;
......@@ -244,6 +247,12 @@ struct gendisk *alloc_disk(int minors)
return disk;
}
struct gendisk *get_disk(struct gendisk *disk)
{
atomic_inc(&disk->disk_dev.refcount);
return disk;
}
void put_disk(struct gendisk *disk)
{
if (disk)
......@@ -251,4 +260,5 @@ void put_disk(struct gendisk *disk)
}
EXPORT_SYMBOL(alloc_disk);
EXPORT_SYMBOL(get_disk);
EXPORT_SYMBOL(put_disk);
......@@ -25,13 +25,17 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
disk = get_gendisk(bdev->bd_dev, &part);
if (!disk)
return -ENXIO;
if (bdev != bdev->bd_contains)
if (bdev != bdev->bd_contains) {
put_disk(disk);
return -EINVAL;
}
if (part)
BUG();
part = p.pno;
if (part <= 0 || part >= disk->minors)
if (part <= 0 || part >= disk->minors) {
put_disk(disk);
return -EINVAL;
}
switch (a.op) {
case BLKPG_ADD_PARTITION:
......@@ -42,34 +46,46 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length;
if (pstart != start || plength != length
|| pstart < 0 || plength < 0)
|| pstart < 0 || plength < 0) {
put_disk(disk);
return -EINVAL;
}
}
/* partition number in use? */
if (disk->part[part - 1].nr_sects != 0)
if (disk->part[part - 1].nr_sects != 0) {
put_disk(disk);
return -EBUSY;
}
/* overlap? */
for (i = 0; i < disk->minors - 1; i++) {
struct hd_struct *s = &disk->part[i];
if (!(start+length <= s->start_sect ||
start >= s->start_sect + s->nr_sects))
start >= s->start_sect + s->nr_sects)) {
put_disk(disk);
return -EBUSY;
}
}
/* all seems OK */
add_partition(disk, part, start, length);
put_disk(disk);
return 0;
case BLKPG_DEL_PARTITION:
if (disk->part[part - 1].nr_sects == 0)
if (disk->part[part - 1].nr_sects == 0) {
put_disk(disk);
return -ENXIO;
}
/* partition in use? Incomplete check for now. */
bdevp = bdget(MKDEV(disk->major, disk->first_minor) + part);
if (!bdevp)
if (!bdevp) {
put_disk(disk);
return -ENOMEM;
}
if (bd_claim(bdevp, &holder) < 0) {
bdput(bdevp);
put_disk(disk);
return -EBUSY;
}
......@@ -80,8 +96,10 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg)
delete_partition(disk, part);
bd_release(bdevp);
bdput(bdevp);
put_disk(disk);
return 0;
default:
put_disk(disk);
return -EINVAL;
}
}
......@@ -92,16 +110,25 @@ static int blkdev_reread_part(struct block_device *bdev)
struct gendisk *disk = get_gendisk(bdev->bd_dev, &part);
int res = 0;
if (!disk || disk->minors == 1 || bdev != bdev->bd_contains)
if (!disk)
return -EINVAL;
if (disk->minors == 1 || bdev != bdev->bd_contains) {
put_disk(disk);
return -EINVAL;
}
if (part)
BUG();
if (!capable(CAP_SYS_ADMIN))
if (!capable(CAP_SYS_ADMIN)) {
put_disk(disk);
return -EACCES;
if (down_trylock(&bdev->bd_sem))
}
if (down_trylock(&bdev->bd_sem)) {
put_disk(disk);
return -EBUSY;
}
res = rescan_partitions(disk, bdev);
up(&bdev->bd_sem);
put_disk(disk);
return res;
}
......
......@@ -542,6 +542,7 @@ int check_disk_change(struct block_device *bdev)
bdops->revalidate(dev);
if (disk && disk->minors > 1)
bdev->bd_invalidated = 1;
put_disk(disk);
return 1;
}
......@@ -553,7 +554,9 @@ int full_check_disk_change(struct block_device *bdev)
BUG();
down(&bdev->bd_sem);
if (check_disk_change(bdev)) {
rescan_partitions(get_gendisk(bdev->bd_dev, &n), bdev);
struct gendisk *disk = get_gendisk(bdev->bd_dev, &n);
rescan_partitions(disk, bdev);
put_disk(disk);
res = 1;
}
up(&bdev->bd_sem);
......@@ -622,13 +625,18 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
struct block_device *disk;
disk = bdget(MKDEV(g->major, g->first_minor));
ret = -ENOMEM;
if (!disk)
if (!disk) {
put_disk(g);
goto out1;
}
ret = blkdev_get(disk, file->f_mode, file->f_flags, BDEV_RAW);
if (ret)
if (ret) {
put_disk(g);
goto out1;
}
bdev->bd_contains = disk;
}
put_disk(g);
}
if (bdev->bd_contains == bdev) {
int part;
......@@ -643,9 +651,11 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
if (bdev->bd_op->open) {
ret = bdev->bd_op->open(inode, file);
if (ret)
if (ret) {
put_disk(g);
goto out2;
}
}
if (!bdev->bd_openers) {
struct backing_dev_info *bdi;
sector_t sect = 0;
......@@ -662,6 +672,7 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
}
if (bdev->bd_invalidated)
rescan_partitions(g, bdev);
put_disk(g);
} else {
down(&bdev->bd_contains->bd_sem);
bdev->bd_contains->bd_part_count++;
......@@ -673,15 +684,17 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file *
inode->i_data.backing_dev_info =
bdev->bd_inode->i_data.backing_dev_info =
bdev->bd_contains->bd_inode->i_data.backing_dev_info;
if (!p->nr_sects) {
if (!(g->flags & GENHD_FL_UP) || !p->nr_sects) {
bdev->bd_contains->bd_part_count--;
up(&bdev->bd_contains->bd_sem);
put_disk(g);
ret = -ENXIO;
goto out2;
}
bdev->bd_queue = bdev->bd_contains->bd_queue;
bdev->bd_offset = p->start_sect;
bd_set_size(bdev, (loff_t) p->nr_sects << 9);
put_disk(g);
}
up(&bdev->bd_contains->bd_sem);
}
......
......@@ -616,6 +616,7 @@ char *partition_name(dev_t dev)
dname->name = NULL;
if (hd)
dname->name = disk_name(hd, part, dname->namebuf);
put_disk(hd);
if (!dname->name) {
sprintf(dname->namebuf, "[dev %s]", kdevname(to_kdev_t(dev)));
dname->name = dname->namebuf;
......
......@@ -266,6 +266,7 @@ extern void add_partition(struct gendisk *, int, sector_t, sector_t);
extern void delete_partition(struct gendisk *, int);
extern struct gendisk *alloc_disk(int minors);
extern struct gendisk *get_disk(struct gendisk *disk);
extern void put_disk(struct gendisk *disk);
/* will go away */
......@@ -273,9 +274,11 @@ extern void blk_set_probe(int major, struct gendisk *(p)(int));
static inline unsigned int disk_index (kdev_t dev)
{
int part;
int part, res;
struct gendisk *g = get_gendisk(kdev_t_to_nr(dev), &part);
return g ? (minor(dev) >> g->minor_shift) : 0;
res = g ? (minor(dev) >> g->minor_shift) : 0;
put_disk(g);
return res;
}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment