diff options
author | Vivek Goyal <vgoyal@redhat.com> | 2012-08-01 06:24:18 -0400 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2012-08-01 06:24:18 -0400 |
commit | c83f6bf98dc1f1a194118b3830706cebbebda8c4 (patch) | |
tree | ea8fbd925584f784164617964a9f025bda16ed15 /block/genhd.c | |
parent | 4638a83e8615de9c16c39dfed234951d0f468cf1 (diff) |
block: add partition resize function to blkpg ioctl
Add a new operation code (BLKPG_RESIZE_PARTITION) to the BLKPG ioctl that
allows altering the size of an existing partition, even if it is currently
in use.
This patch converts hd_struct->nr_sects into sequence counter because
One might extend a partition while IO is happening to it and update of
nr_sects can be non-atomic on 32bit machines with 64bit sector_t. This
can lead to issues like reading inconsistent size of a partition. Sequence
counter have been used so that readers don't have to take bdev mutex lock
as we call sector_in_part() very frequently.
Now all the access to hd_struct->nr_sects should happen using sequence
counter read/update helper functions part_nr_sects_read/part_nr_sects_write.
There is one exception though, set_capacity()/get_capacity(). I think
theoritically race should exist there too but this patch does not
modify set_capacity()/get_capacity() due to sheer number of call sites
and I am afraid that change might break something. I have left that as a
TODO item. We can handle it later if need be. This patch does not introduce
any new races as such w.r.t set_capacity()/get_capacity().
v2: Add CONFIG_LBDAF test to UP preempt case as suggested by Phillip.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Phillip Susi <psusi@ubuntu.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'block/genhd.c')
-rw-r--r-- | block/genhd.c | 20 |
1 files changed, 15 insertions, 5 deletions
diff --git a/block/genhd.c b/block/genhd.c index 9cf5583c90ff..cac7366957c3 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -154,7 +154,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) | |||
154 | part = rcu_dereference(ptbl->part[piter->idx]); | 154 | part = rcu_dereference(ptbl->part[piter->idx]); |
155 | if (!part) | 155 | if (!part) |
156 | continue; | 156 | continue; |
157 | if (!part->nr_sects && | 157 | if (!part_nr_sects_read(part) && |
158 | !(piter->flags & DISK_PITER_INCL_EMPTY) && | 158 | !(piter->flags & DISK_PITER_INCL_EMPTY) && |
159 | !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 && | 159 | !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 && |
160 | piter->idx == 0)) | 160 | piter->idx == 0)) |
@@ -191,7 +191,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); | |||
191 | static inline int sector_in_part(struct hd_struct *part, sector_t sector) | 191 | static inline int sector_in_part(struct hd_struct *part, sector_t sector) |
192 | { | 192 | { |
193 | return part->start_sect <= sector && | 193 | return part->start_sect <= sector && |
194 | sector < part->start_sect + part->nr_sects; | 194 | sector < part->start_sect + part_nr_sects_read(part); |
195 | } | 195 | } |
196 | 196 | ||
197 | /** | 197 | /** |
@@ -769,8 +769,8 @@ void __init printk_all_partitions(void) | |||
769 | 769 | ||
770 | printk("%s%s %10llu %s %s", is_part0 ? "" : " ", | 770 | printk("%s%s %10llu %s %s", is_part0 ? "" : " ", |
771 | bdevt_str(part_devt(part), devt_buf), | 771 | bdevt_str(part_devt(part), devt_buf), |
772 | (unsigned long long)part->nr_sects >> 1, | 772 | (unsigned long long)part_nr_sects_read(part) >> 1 |
773 | disk_name(disk, part->partno, name_buf), | 773 | , disk_name(disk, part->partno, name_buf), |
774 | uuid_buf); | 774 | uuid_buf); |
775 | if (is_part0) { | 775 | if (is_part0) { |
776 | if (disk->driverfs_dev != NULL && | 776 | if (disk->driverfs_dev != NULL && |
@@ -862,7 +862,7 @@ static int show_partition(struct seq_file *seqf, void *v) | |||
862 | while ((part = disk_part_iter_next(&piter))) | 862 | while ((part = disk_part_iter_next(&piter))) |
863 | seq_printf(seqf, "%4d %7d %10llu %s\n", | 863 | seq_printf(seqf, "%4d %7d %10llu %s\n", |
864 | MAJOR(part_devt(part)), MINOR(part_devt(part)), | 864 | MAJOR(part_devt(part)), MINOR(part_devt(part)), |
865 | (unsigned long long)part->nr_sects >> 1, | 865 | (unsigned long long)part_nr_sects_read(part) >> 1, |
866 | disk_name(sgp, part->partno, buf)); | 866 | disk_name(sgp, part->partno, buf)); |
867 | disk_part_iter_exit(&piter); | 867 | disk_part_iter_exit(&piter); |
868 | 868 | ||
@@ -1268,6 +1268,16 @@ struct gendisk *alloc_disk_node(int minors, int node_id) | |||
1268 | } | 1268 | } |
1269 | disk->part_tbl->part[0] = &disk->part0; | 1269 | disk->part_tbl->part[0] = &disk->part0; |
1270 | 1270 | ||
1271 | /* | ||
1272 | * set_capacity() and get_capacity() currently don't use | ||
1273 | * seqcounter to read/update the part0->nr_sects. Still init | ||
1274 | * the counter as we can read the sectors in IO submission | ||
1275 | * patch using seqence counters. | ||
1276 | * | ||
1277 | * TODO: Ideally set_capacity() and get_capacity() should be | ||
1278 | * converted to make use of bd_mutex and sequence counters. | ||
1279 | */ | ||
1280 | seqcount_init(&disk->part0.nr_sects_seq); | ||
1271 | hd_ref_init(&disk->part0); | 1281 | hd_ref_init(&disk->part0); |
1272 | 1282 | ||
1273 | disk->minors = minors; | 1283 | disk->minors = minors; |