clear_bit(BIO_UPTODATE, &bio->bi_flags);
}
+ if (bio->bi_private)
+ complete(bio->bi_private);
+
bio_put(bio);
}
* @sector: start sector
* @nr_sects: number of sectors to discard
* @gfp_mask: memory allocation flags (for bio_alloc)
+ * @flags: DISCARD_FL_* flags to control behaviour
*
* Description:
- * Issue a discard request for the sectors in question. Does not wait.
+ * Issue a discard request for the sectors in question.
*/
-int blkdev_issue_discard(struct block_device *bdev,
- sector_t sector, sector_t nr_sects, gfp_t gfp_mask)
+int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
+ sector_t nr_sects, gfp_t gfp_mask, int flags)
{
- struct request_queue *q;
- struct bio *bio;
+ DECLARE_COMPLETION_ONSTACK(wait);
+ struct request_queue *q = bdev_get_queue(bdev);
+ int type = flags & DISCARD_FL_BARRIER ?
+ DISCARD_BARRIER : DISCARD_NOBARRIER;
int ret = 0;
- if (bdev->bd_disk == NULL)
- return -ENXIO;
-
- q = bdev_get_queue(bdev);
if (!q)
return -ENXIO;
return -EOPNOTSUPP;
while (nr_sects && !ret) {
- bio = bio_alloc(gfp_mask, 0);
+ struct bio *bio = bio_alloc(gfp_mask, 0);
if (!bio)
return -ENOMEM;
bio->bi_end_io = blkdev_discard_end_io;
bio->bi_bdev = bdev;
+ if (flags & DISCARD_FL_WAIT)
+ bio->bi_private = &wait;
bio->bi_sector = sector;
bio->bi_size = nr_sects << 9;
nr_sects = 0;
}
+
bio_get(bio);
- submit_bio(DISCARD_BARRIER, bio);
+ submit_bio(type, bio);
+
+ if (flags & DISCARD_FL_WAIT)
+ wait_for_completion(&wait);
- /* Check if it failed immediately */
if (bio_flagged(bio, BIO_EOPNOTSUPP))
ret = -EOPNOTSUPP;
else if (!bio_flagged(bio, BIO_UPTODATE))
return res;
}
-static void blk_ioc_discard_endio(struct bio *bio, int err)
-{
- if (err) {
- if (err == -EOPNOTSUPP)
- set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
- clear_bit(BIO_UPTODATE, &bio->bi_flags);
- }
- complete(bio->bi_private);
-}
-
static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
uint64_t len)
{
- struct request_queue *q = bdev_get_queue(bdev);
- int ret = 0;
-
if (start & 511)
return -EINVAL;
if (len & 511)
if (start + len > (bdev->bd_inode->i_size >> 9))
return -EINVAL;
-
- if (!q->prepare_discard_fn)
- return -EOPNOTSUPP;
-
- while (len && !ret) {
- DECLARE_COMPLETION_ONSTACK(wait);
- struct bio *bio;
-
- bio = bio_alloc(GFP_KERNEL, 0);
-
- bio->bi_end_io = blk_ioc_discard_endio;
- bio->bi_bdev = bdev;
- bio->bi_private = &wait;
- bio->bi_sector = start;
-
- if (len > queue_max_hw_sectors(q)) {
- bio->bi_size = queue_max_hw_sectors(q) << 9;
- len -= queue_max_hw_sectors(q);
- start += queue_max_hw_sectors(q);
- } else {
- bio->bi_size = len << 9;
- len = 0;
- }
- submit_bio(DISCARD_NOBARRIER, bio);
-
- wait_for_completion(&wait);
-
- if (bio_flagged(bio, BIO_EOPNOTSUPP))
- ret = -EOPNOTSUPP;
- else if (!bio_flagged(bio, BIO_UPTODATE))
- ret = -EIO;
- bio_put(bio);
- }
- return ret;
+ return blkdev_issue_discard(bdev, start, len, GFP_KERNEL,
+ DISCARD_FL_WAIT);
}
static int put_ushort(unsigned long arg, unsigned short val)
static void btrfs_issue_discard(struct block_device *bdev,
u64 start, u64 len)
{
- blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL);
+ blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL,
+ DISCARD_FL_BARRIER);
}
#endif
goto start_new_extent;
if ((start + nr_sects) != blk) {
rv = blkdev_issue_discard(bdev, start,
- nr_sects, GFP_NOFS);
+ nr_sects, GFP_NOFS,
+ DISCARD_FL_BARRIER);
if (rv)
goto fail;
nr_sects = 0;
}
}
if (nr_sects) {
- rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS);
+ rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS,
+ DISCARD_FL_BARRIER);
if (rv)
goto fail;
}
}
extern int blkdev_issue_flush(struct block_device *, sector_t *);
-extern int blkdev_issue_discard(struct block_device *,
- sector_t sector, sector_t nr_sects, gfp_t);
+#define DISCARD_FL_WAIT 0x01 /* wait for completion */
+#define DISCARD_FL_BARRIER 0x02 /* issue DISCARD_BARRIER request */
+extern int blkdev_issue_discard(struct block_device *, sector_t sector,
+ sector_t nr_sects, gfp_t, int flags);
static inline int sb_issue_discard(struct super_block *sb,
sector_t block, sector_t nr_blocks)
{
block <<= (sb->s_blocksize_bits - 9);
nr_blocks <<= (sb->s_blocksize_bits - 9);
- return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_KERNEL);
+ return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_KERNEL,
+ DISCARD_FL_BARRIER);
}
extern int blk_verify_command(unsigned char *cmd, fmode_t has_write_perm);
}
err = blkdev_issue_discard(si->bdev, start_block,
- nr_blocks, GFP_KERNEL);
+ nr_blocks, GFP_KERNEL,
+ DISCARD_FL_BARRIER);
if (err)
break;
start_block <<= PAGE_SHIFT - 9;
nr_blocks <<= PAGE_SHIFT - 9;
if (blkdev_issue_discard(si->bdev, start_block,
- nr_blocks, GFP_NOIO))
+ nr_blocks, GFP_NOIO,
+ DISCARD_FL_BARRIER))
break;
}