aboutsummaryrefslogtreecommitdiffstats
path: root/block/ioctl.c
diff options
context:
space:
mode:
authorDavid Woodhouse <David.Woodhouse@intel.com>2008-08-11 10:58:42 -0400
committerJens Axboe <jens.axboe@oracle.com>2008-10-09 02:56:02 -0400
commitd30a2605be9d5132d95944916e8f578fcfe4f976 (patch)
tree91a8161cb09536ab838ef47fceabb0da0bea9256 /block/ioctl.c
parent2ebca85abcfcbaaf1c0b242e39fc88ad3da90090 (diff)
Add BLKDISCARD ioctl to allow userspace to discard sectors
We may well want mkfs tools to use this to mark the whole device as unwanted before they format it, for example. The ioctl takes a pair of uint64_ts, which are start offset and length in _bytes_. Although at the moment it might make sense for them both to be in 512-byte sectors, I don't want to limit the ABI to that. Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'block/ioctl.c')
-rw-r--r--block/ioctl.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/block/ioctl.c b/block/ioctl.c
index 77185e5c026a..342298bb6080 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -111,6 +111,69 @@ static int blkdev_reread_part(struct block_device *bdev)
111 return res; 111 return res;
112} 112}
113 113
114static void blk_ioc_discard_endio(struct bio *bio, int err)
115{
116 if (err) {
117 if (err == -EOPNOTSUPP)
118 set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
119 clear_bit(BIO_UPTODATE, &bio->bi_flags);
120 }
121 complete(bio->bi_private);
122}
123
124static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
125 uint64_t len)
126{
127 struct request_queue *q = bdev_get_queue(bdev);
128 int ret = 0;
129
130 if (start & 511)
131 return -EINVAL;
132 if (len & 511)
133 return -EINVAL;
134 start >>= 9;
135 len >>= 9;
136
137 if (start + len > (bdev->bd_inode->i_size >> 9))
138 return -EINVAL;
139
140 if (!q->prepare_discard_fn)
141 return -EOPNOTSUPP;
142
143 while (len && !ret) {
144 DECLARE_COMPLETION_ONSTACK(wait);
145 struct bio *bio;
146
147 bio = bio_alloc(GFP_KERNEL, 0);
148 if (!bio)
149 return -ENOMEM;
150
151 bio->bi_end_io = blk_ioc_discard_endio;
152 bio->bi_bdev = bdev;
153 bio->bi_private = &wait;
154 bio->bi_sector = start;
155
156 if (len > q->max_hw_sectors) {
157 bio->bi_size = q->max_hw_sectors << 9;
158 len -= q->max_hw_sectors;
159 start += q->max_hw_sectors;
160 } else {
161 bio->bi_size = len << 9;
162 len = 0;
163 }
164 submit_bio(WRITE_DISCARD, bio);
165
166 wait_for_completion(&wait);
167
168 if (bio_flagged(bio, BIO_EOPNOTSUPP))
169 ret = -EOPNOTSUPP;
170 else if (!bio_flagged(bio, BIO_UPTODATE))
171 ret = -EIO;
172 bio_put(bio);
173 }
174 return ret;
175}
176
114static int put_ushort(unsigned long arg, unsigned short val) 177static int put_ushort(unsigned long arg, unsigned short val)
115{ 178{
116 return put_user(val, (unsigned short __user *)arg); 179 return put_user(val, (unsigned short __user *)arg);
@@ -258,6 +321,19 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
258 set_device_ro(bdev, n); 321 set_device_ro(bdev, n);
259 unlock_kernel(); 322 unlock_kernel();
260 return 0; 323 return 0;
324
325 case BLKDISCARD: {
326 uint64_t range[2];
327
328 if (!(file->f_mode & FMODE_WRITE))
329 return -EBADF;
330
331 if (copy_from_user(range, (void __user *)arg, sizeof(range)))
332 return -EFAULT;
333
334 return blk_ioctl_discard(bdev, range[0], range[1]);
335 }
336
261 case HDIO_GETGEO: { 337 case HDIO_GETGEO: {
262 struct hd_geometry geo; 338 struct hd_geometry geo;
263 339