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/ioctl.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/ioctl.c')
-rw-r--r-- | block/ioctl.c | 59 |
1 files changed, 56 insertions, 3 deletions
diff --git a/block/ioctl.c b/block/ioctl.c index ba15b2dbfb98..4476e0e85d16 100644 --- a/block/ioctl.c +++ b/block/ioctl.c | |||
@@ -13,7 +13,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user | |||
13 | { | 13 | { |
14 | struct block_device *bdevp; | 14 | struct block_device *bdevp; |
15 | struct gendisk *disk; | 15 | struct gendisk *disk; |
16 | struct hd_struct *part; | 16 | struct hd_struct *part, *lpart; |
17 | struct blkpg_ioctl_arg a; | 17 | struct blkpg_ioctl_arg a; |
18 | struct blkpg_partition p; | 18 | struct blkpg_partition p; |
19 | struct disk_part_iter piter; | 19 | struct disk_part_iter piter; |
@@ -36,8 +36,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user | |||
36 | case BLKPG_ADD_PARTITION: | 36 | case BLKPG_ADD_PARTITION: |
37 | start = p.start >> 9; | 37 | start = p.start >> 9; |
38 | length = p.length >> 9; | 38 | length = p.length >> 9; |
39 | /* check for fit in a hd_struct */ | 39 | /* check for fit in a hd_struct */ |
40 | if (sizeof(sector_t) == sizeof(long) && | 40 | if (sizeof(sector_t) == sizeof(long) && |
41 | sizeof(long long) > sizeof(long)) { | 41 | sizeof(long long) > sizeof(long)) { |
42 | long pstart = start, plength = length; | 42 | long pstart = start, plength = length; |
43 | if (pstart != start || plength != length | 43 | if (pstart != start || plength != length |
@@ -92,6 +92,59 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user | |||
92 | bdput(bdevp); | 92 | bdput(bdevp); |
93 | 93 | ||
94 | return 0; | 94 | return 0; |
95 | case BLKPG_RESIZE_PARTITION: | ||
96 | start = p.start >> 9; | ||
97 | /* new length of partition in bytes */ | ||
98 | length = p.length >> 9; | ||
99 | /* check for fit in a hd_struct */ | ||
100 | if (sizeof(sector_t) == sizeof(long) && | ||
101 | sizeof(long long) > sizeof(long)) { | ||
102 | long pstart = start, plength = length; | ||
103 | if (pstart != start || plength != length | ||
104 | || pstart < 0 || plength < 0) | ||
105 | return -EINVAL; | ||
106 | } | ||
107 | part = disk_get_part(disk, partno); | ||
108 | if (!part) | ||
109 | return -ENXIO; | ||
110 | bdevp = bdget(part_devt(part)); | ||
111 | if (!bdevp) { | ||
112 | disk_put_part(part); | ||
113 | return -ENOMEM; | ||
114 | } | ||
115 | mutex_lock(&bdevp->bd_mutex); | ||
116 | mutex_lock_nested(&bdev->bd_mutex, 1); | ||
117 | if (start != part->start_sect) { | ||
118 | mutex_unlock(&bdevp->bd_mutex); | ||
119 | mutex_unlock(&bdev->bd_mutex); | ||
120 | bdput(bdevp); | ||
121 | disk_put_part(part); | ||
122 | return -EINVAL; | ||
123 | } | ||
124 | /* overlap? */ | ||
125 | disk_part_iter_init(&piter, disk, | ||
126 | DISK_PITER_INCL_EMPTY); | ||
127 | while ((lpart = disk_part_iter_next(&piter))) { | ||
128 | if (lpart->partno != partno && | ||
129 | !(start + length <= lpart->start_sect || | ||
130 | start >= lpart->start_sect + lpart->nr_sects) | ||
131 | ) { | ||
132 | disk_part_iter_exit(&piter); | ||
133 | mutex_unlock(&bdevp->bd_mutex); | ||
134 | mutex_unlock(&bdev->bd_mutex); | ||
135 | bdput(bdevp); | ||
136 | disk_put_part(part); | ||
137 | return -EBUSY; | ||
138 | } | ||
139 | } | ||
140 | disk_part_iter_exit(&piter); | ||
141 | part_nr_sects_write(part, (sector_t)length); | ||
142 | i_size_write(bdevp->bd_inode, p.length); | ||
143 | mutex_unlock(&bdevp->bd_mutex); | ||
144 | mutex_unlock(&bdev->bd_mutex); | ||
145 | bdput(bdevp); | ||
146 | disk_put_part(part); | ||
147 | return 0; | ||
95 | default: | 148 | default: |
96 | return -EINVAL; | 149 | return -EINVAL; |
97 | } | 150 | } |