Commit d30a2605be9d5132d95944916e8f578fcfe4f976

Authored by David Woodhouse
Committed by Jens Axboe
1 parent 2ebca85abc

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>

Showing 3 changed files with 78 additions and 0 deletions Side-by-side Diff

block/compat_ioctl.c
... ... @@ -788,6 +788,7 @@
788 788 return compat_hdio_getgeo(disk, bdev, compat_ptr(arg));
789 789 case BLKFLSBUF:
790 790 case BLKROSET:
  791 + case BLKDISCARD:
791 792 /*
792 793 * the ones below are implemented in blkdev_locked_ioctl,
793 794 * but we call blkdev_ioctl, which gets the lock for us
... ... @@ -111,6 +111,69 @@
111 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 177 static int put_ushort(unsigned long arg, unsigned short val)
115 178 {
116 179 return put_user(val, (unsigned short __user *)arg);
... ... @@ -258,6 +321,19 @@
258 321 set_device_ro(bdev, n);
259 322 unlock_kernel();
260 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 337 case HDIO_GETGEO: {
262 338 struct hd_geometry geo;
263 339  
... ... @@ -223,6 +223,7 @@
223 223 #define BLKTRACESTART _IO(0x12,116)
224 224 #define BLKTRACESTOP _IO(0x12,117)
225 225 #define BLKTRACETEARDOWN _IO(0x12,118)
  226 +#define BLKDISCARD _IO(0x12,119)
226 227  
227 228 #define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
228 229 #define FIBMAP _IO(0x00,1) /* bmap access */