aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--block/compat_ioctl.c1
-rw-r--r--block/ioctl.c76
-rw-r--r--include/linux/fs.h1
3 files changed, 78 insertions, 0 deletions
diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c
index c23177e4623f..1e559fba7bdf 100644
--- a/block/compat_ioctl.c
+++ b/block/compat_ioctl.c
@@ -788,6 +788,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
788 return compat_hdio_getgeo(disk, bdev, compat_ptr(arg)); 788 return compat_hdio_getgeo(disk, bdev, compat_ptr(arg));
789 case BLKFLSBUF: 789 case BLKFLSBUF:
790 case BLKROSET: 790 case BLKROSET:
791 case BLKDISCARD:
791 /* 792 /*
792 * the ones below are implemented in blkdev_locked_ioctl, 793 * the ones below are implemented in blkdev_locked_ioctl,
793 * but we call blkdev_ioctl, which gets the lock for us 794 * but we call blkdev_ioctl, which gets the lock for us
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
diff --git a/include/linux/fs.h b/include/linux/fs.h
index eb0131319134..88358ca6af25 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -223,6 +223,7 @@ extern int dir_notify_enable;
223#define BLKTRACESTART _IO(0x12,116) 223#define BLKTRACESTART _IO(0x12,116)
224#define BLKTRACESTOP _IO(0x12,117) 224#define BLKTRACESTOP _IO(0x12,117)
225#define BLKTRACETEARDOWN _IO(0x12,118) 225#define BLKTRACETEARDOWN _IO(0x12,118)
226#define BLKDISCARD _IO(0x12,119)
226 227
227#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */ 228#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
228#define FIBMAP _IO(0x00,1) /* bmap access */ 229#define FIBMAP _IO(0x00,1) /* bmap access */