diff options
-rw-r--r-- | block/compat_ioctl.c | 1 | ||||
-rw-r--r-- | block/ioctl.c | 76 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
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 | ||
114 | static 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 | |||
124 | static 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 | |||
114 | static int put_ushort(unsigned long arg, unsigned short val) | 177 | static 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 */ |