summaryrefslogtreecommitdiff
path: root/block/partitions/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/partitions/core.c')
-rw-r--r--block/partitions/core.c250
1 files changed, 81 insertions, 169 deletions
diff --git a/block/partitions/core.c b/block/partitions/core.c
index a02e22411594..deca253583bd 100644
--- a/block/partitions/core.c
+++ b/block/partitions/core.c
@@ -85,6 +85,13 @@ static int (*check_part[])(struct parsed_partitions *) = {
NULL
};
+static void bdev_set_nr_sectors(struct block_device *bdev, sector_t sectors)
+{
+ spin_lock(&bdev->bd_size_lock);
+ i_size_write(bdev->bd_inode, (loff_t)sectors << SECTOR_SHIFT);
+ spin_unlock(&bdev->bd_size_lock);
+}
+
static struct parsed_partitions *allocate_partitions(struct gendisk *hd)
{
struct parsed_partitions *state;
@@ -175,44 +182,39 @@ static struct parsed_partitions *check_partition(struct gendisk *hd,
static ssize_t part_partition_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hd_struct *p = dev_to_part(dev);
-
- return sprintf(buf, "%d\n", p->partno);
+ return sprintf(buf, "%d\n", dev_to_bdev(dev)->bd_partno);
}
static ssize_t part_start_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hd_struct *p = dev_to_part(dev);
-
- return sprintf(buf, "%llu\n",(unsigned long long)p->start_sect);
+ return sprintf(buf, "%llu\n", dev_to_bdev(dev)->bd_start_sect);
}
static ssize_t part_ro_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hd_struct *p = dev_to_part(dev);
- return sprintf(buf, "%d\n", p->policy ? 1 : 0);
+ return sprintf(buf, "%d\n", dev_to_bdev(dev)->bd_read_only);
}
static ssize_t part_alignment_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hd_struct *p = dev_to_part(dev);
+ struct block_device *bdev = dev_to_bdev(dev);
return sprintf(buf, "%u\n",
- queue_limit_alignment_offset(&part_to_disk(p)->queue->limits,
- p->start_sect));
+ queue_limit_alignment_offset(&bdev->bd_disk->queue->limits,
+ bdev->bd_start_sect));
}
static ssize_t part_discard_alignment_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hd_struct *p = dev_to_part(dev);
+ struct block_device *bdev = dev_to_bdev(dev);
return sprintf(buf, "%u\n",
- queue_limit_discard_alignment(&part_to_disk(p)->queue->limits,
- p->start_sect));
+ queue_limit_discard_alignment(&bdev->bd_disk->queue->limits,
+ bdev->bd_start_sect));
}
static DEVICE_ATTR(partition, 0444, part_partition_show, NULL);
@@ -257,19 +259,17 @@ static const struct attribute_group *part_attr_groups[] = {
static void part_release(struct device *dev)
{
- struct hd_struct *p = dev_to_part(dev);
blk_free_devt(dev->devt);
- hd_free_part(p);
- kfree(p);
+ bdput(dev_to_bdev(dev));
}
static int part_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- struct hd_struct *part = dev_to_part(dev);
+ struct block_device *part = dev_to_bdev(dev);
- add_uevent_var(env, "PARTN=%u", part->partno);
- if (part->info && part->info->volname[0])
- add_uevent_var(env, "PARTNAME=%s", part->info->volname);
+ add_uevent_var(env, "PARTN=%u", part->bd_partno);
+ if (part->bd_meta_info && part->bd_meta_info->volname[0])
+ add_uevent_var(env, "PARTNAME=%s", part->bd_meta_info->volname);
return 0;
}
@@ -280,73 +280,29 @@ struct device_type part_type = {
.uevent = part_uevent,
};
-static void hd_struct_free_work(struct work_struct *work)
-{
- struct hd_struct *part =
- container_of(to_rcu_work(work), struct hd_struct, rcu_work);
- struct gendisk *disk = part_to_disk(part);
-
- /*
- * Release the disk reference acquired in delete_partition here.
- * We can't release it in hd_struct_free because the final put_device
- * needs process context and thus can't be run directly from a
- * percpu_ref ->release handler.
- */
- put_device(disk_to_dev(disk));
-
- part->start_sect = 0;
- part->nr_sects = 0;
- part_stat_set_all(part, 0);
- put_device(part_to_dev(part));
-}
-
-static void hd_struct_free(struct percpu_ref *ref)
-{
- struct hd_struct *part = container_of(ref, struct hd_struct, ref);
- struct gendisk *disk = part_to_disk(part);
- struct disk_part_tbl *ptbl =
- rcu_dereference_protected(disk->part_tbl, 1);
-
- rcu_assign_pointer(ptbl->last_lookup, NULL);
-
- INIT_RCU_WORK(&part->rcu_work, hd_struct_free_work);
- queue_rcu_work(system_wq, &part->rcu_work);
-}
-
-int hd_ref_init(struct hd_struct *part)
-{
- if (percpu_ref_init(&part->ref, hd_struct_free, 0, GFP_KERNEL))
- return -ENOMEM;
- return 0;
-}
-
/*
* Must be called either with bd_mutex held, before a disk can be opened or
* after all disk users are gone.
*/
-void delete_partition(struct hd_struct *part)
+void delete_partition(struct block_device *part)
{
- struct gendisk *disk = part_to_disk(part);
+ struct gendisk *disk = part->bd_disk;
struct disk_part_tbl *ptbl =
rcu_dereference_protected(disk->part_tbl, 1);
- /*
- * ->part_tbl is referenced in this part's release handler, so
- * we have to hold the disk device
- */
- get_device(disk_to_dev(disk));
- rcu_assign_pointer(ptbl->part[part->partno], NULL);
- kobject_put(part->holder_dir);
- device_del(part_to_dev(part));
+ rcu_assign_pointer(ptbl->part[part->bd_partno], NULL);
+ rcu_assign_pointer(ptbl->last_lookup, NULL);
+
+ kobject_put(part->bd_holder_dir);
+ device_del(&part->bd_device);
/*
- * Remove gendisk pointer from idr so that it cannot be looked up
- * while RCU period before freeing gendisk is running to prevent
- * use-after-free issues. Note that the device number stays
- * "in-use" until we really free the gendisk.
+ * Remove the block device from the inode hash, so that it cannot be
+ * looked up any more even when openers still hold references.
*/
- blk_invalidate_devt(part_devt(part));
- percpu_ref_kill(&part->ref);
+ remove_inode_hash(part->bd_inode);
+
+ put_device(&part->bd_device);
}
static ssize_t whole_disk_show(struct device *dev,
@@ -360,14 +316,14 @@ static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
* Must be called either with bd_mutex held, before a disk can be opened or
* after all disk users are gone.
*/
-static struct hd_struct *add_partition(struct gendisk *disk, int partno,
+static struct block_device *add_partition(struct gendisk *disk, int partno,
sector_t start, sector_t len, int flags,
struct partition_meta_info *info)
{
- struct hd_struct *p;
dev_t devt = MKDEV(0, 0);
struct device *ddev = disk_to_dev(disk);
struct device *pdev;
+ struct block_device *bdev;
struct disk_part_tbl *ptbl;
const char *dname;
int err;
@@ -398,36 +354,22 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
if (ptbl->part[partno])
return ERR_PTR(-EBUSY);
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- return ERR_PTR(-EBUSY);
-
- p->dkstats = alloc_percpu(struct disk_stats);
- if (!p->dkstats) {
- err = -ENOMEM;
- goto out_free;
- }
-
- hd_sects_seq_init(p);
- pdev = part_to_dev(p);
+ bdev = bdev_alloc(disk, partno);
+ if (!bdev)
+ return ERR_PTR(-ENOMEM);
- p->start_sect = start;
- p->nr_sects = len;
- p->partno = partno;
- p->policy = get_disk_ro(disk);
+ bdev->bd_start_sect = start;
+ bdev_set_nr_sectors(bdev, len);
+ bdev->bd_read_only = get_disk_ro(disk);
if (info) {
- struct partition_meta_info *pinfo;
-
- pinfo = kzalloc_node(sizeof(*pinfo), GFP_KERNEL, disk->node_id);
- if (!pinfo) {
- err = -ENOMEM;
- goto out_free_stats;
- }
- memcpy(pinfo, info, sizeof(*info));
- p->info = pinfo;
+ err = -ENOMEM;
+ bdev->bd_meta_info = kmemdup(info, sizeof(*info), GFP_KERNEL);
+ if (!bdev->bd_meta_info)
+ goto out_bdput;
}
+ pdev = &bdev->bd_device;
dname = dev_name(ddev);
if (isdigit(dname[strlen(dname) - 1]))
dev_set_name(pdev, "%sp%d", dname, partno);
@@ -439,9 +381,9 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
pdev->type = &part_type;
pdev->parent = ddev;
- err = blk_alloc_devt(p, &devt);
+ err = blk_alloc_devt(bdev, &devt);
if (err)
- goto out_free_info;
+ goto out_bdput;
pdev->devt = devt;
/* delay uevent until 'holders' subdir is created */
@@ -451,8 +393,8 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
goto out_put;
err = -ENOMEM;
- p->holder_dir = kobject_create_and_add("holders", &pdev->kobj);
- if (!p->holder_dir)
+ bdev->bd_holder_dir = kobject_create_and_add("holders", &pdev->kobj);
+ if (!bdev->bd_holder_dir)
goto out_del;
dev_set_uevent_suppress(pdev, 0);
@@ -462,32 +404,20 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
goto out_del;
}
- err = hd_ref_init(p);
- if (err) {
- if (flags & ADDPART_FLAG_WHOLEDISK)
- goto out_remove_file;
- goto out_del;
- }
-
/* everything is up and running, commence */
- rcu_assign_pointer(ptbl->part[partno], p);
+ bdev_add(bdev, devt);
+ rcu_assign_pointer(ptbl->part[partno], bdev);
/* suppress uevent if the disk suppresses it */
if (!dev_get_uevent_suppress(ddev))
kobject_uevent(&pdev->kobj, KOBJ_ADD);
- return p;
-
-out_free_info:
- kfree(p->info);
-out_free_stats:
- free_percpu(p->dkstats);
-out_free:
- kfree(p);
+ return bdev;
+
+out_bdput:
+ bdput(bdev);
return ERR_PTR(err);
-out_remove_file:
- device_remove_file(pdev, &dev_attr_whole_disk);
out_del:
- kobject_put(p->holder_dir);
+ kobject_put(bdev->bd_holder_dir);
device_del(pdev);
out_put:
put_device(pdev);
@@ -498,14 +428,14 @@ static bool partition_overlaps(struct gendisk *disk, sector_t start,
sector_t length, int skip_partno)
{
struct disk_part_iter piter;
- struct hd_struct *part;
+ struct block_device *part;
bool overlap = false;
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
while ((part = disk_part_iter_next(&piter))) {
- if (part->partno == skip_partno ||
- start >= part->start_sect + part->nr_sects ||
- start + length <= part->start_sect)
+ if (part->bd_partno == skip_partno ||
+ start >= part->bd_start_sect + bdev_nr_sectors(part) ||
+ start + length <= part->bd_start_sect)
continue;
overlap = true;
break;
@@ -518,7 +448,7 @@ static bool partition_overlaps(struct gendisk *disk, sector_t start,
int bdev_add_partition(struct block_device *bdev, int partno,
sector_t start, sector_t length)
{
- struct hd_struct *part;
+ struct block_device *part;
mutex_lock(&bdev->bd_mutex);
if (partition_overlaps(bdev->bd_disk, start, length, -1)) {
@@ -534,77 +464,59 @@ int bdev_add_partition(struct block_device *bdev, int partno,
int bdev_del_partition(struct block_device *bdev, int partno)
{
- struct block_device *bdevp;
- struct hd_struct *part = NULL;
+ struct block_device *part;
int ret;
- bdevp = bdget_disk(bdev->bd_disk, partno);
- if (!bdevp)
+ part = bdget_disk(bdev->bd_disk, partno);
+ if (!part)
return -ENXIO;
- mutex_lock(&bdevp->bd_mutex);
+ mutex_lock(&part->bd_mutex);
mutex_lock_nested(&bdev->bd_mutex, 1);
- ret = -ENXIO;
- part = disk_get_part(bdev->bd_disk, partno);
- if (!part)
- goto out_unlock;
-
ret = -EBUSY;
- if (bdevp->bd_openers)
+ if (part->bd_openers)
goto out_unlock;
- sync_blockdev(bdevp);
- invalidate_bdev(bdevp);
+ sync_blockdev(part);
+ invalidate_bdev(part);
delete_partition(part);
ret = 0;
out_unlock:
mutex_unlock(&bdev->bd_mutex);
- mutex_unlock(&bdevp->bd_mutex);
- bdput(bdevp);
- if (part)
- disk_put_part(part);
+ mutex_unlock(&part->bd_mutex);
+ bdput(part);
return ret;
}
int bdev_resize_partition(struct block_device *bdev, int partno,
sector_t start, sector_t length)
{
- struct block_device *bdevp;
- struct hd_struct *part;
+ struct block_device *part;
int ret = 0;
- part = disk_get_part(bdev->bd_disk, partno);
+ part = bdget_disk(bdev->bd_disk, partno);
if (!part)
return -ENXIO;
- ret = -ENOMEM;
- bdevp = bdget_part(part);
- if (!bdevp)
- goto out_put_part;
-
- mutex_lock(&bdevp->bd_mutex);
+ mutex_lock(&part->bd_mutex);
mutex_lock_nested(&bdev->bd_mutex, 1);
-
ret = -EINVAL;
- if (start != part->start_sect)
+ if (start != part->bd_start_sect)
goto out_unlock;
ret = -EBUSY;
if (partition_overlaps(bdev->bd_disk, start, length, partno))
goto out_unlock;
- part_nr_sects_write(part, length);
- bd_set_nr_sectors(bdevp, length);
+ bdev_set_nr_sectors(part, length);
ret = 0;
out_unlock:
- mutex_unlock(&bdevp->bd_mutex);
+ mutex_unlock(&part->bd_mutex);
mutex_unlock(&bdev->bd_mutex);
- bdput(bdevp);
-out_put_part:
- disk_put_part(part);
+ bdput(part);
return ret;
}
@@ -627,7 +539,7 @@ static bool disk_unlock_native_capacity(struct gendisk *disk)
int blk_drop_partitions(struct block_device *bdev)
{
struct disk_part_iter piter;
- struct hd_struct *part;
+ struct block_device *part;
if (bdev->bd_part_count)
return -EBUSY;
@@ -652,7 +564,7 @@ static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev,
{
sector_t size = state->parts[p].size;
sector_t from = state->parts[p].from;
- struct hd_struct *part;
+ struct block_device *part;
if (!size)
return true;
@@ -692,7 +604,7 @@ static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev,
if (IS_BUILTIN(CONFIG_BLK_DEV_MD) &&
(state->parts[p].flags & ADDPART_FLAG_RAID))
- md_autodetect_dev(part_to_dev(part)->devt);
+ md_autodetect_dev(part->bd_dev);
return true;
}