diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2008-08-05 13:01:53 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2008-10-09 02:56:01 -0400 |
commit | fb2dce862d9f9a68e6b9374579056ec9eca02a63 (patch) | |
tree | 888e0fd7248c9329fa1aa3981043a2dc2457d488 /block/blk-barrier.c | |
parent | d628eaef310533767ce68664873869c2d7f78f09 (diff) |
Add 'discard' request handling
Some block devices benefit from a hint that they can forget the contents
of certain sectors. Add basic support for this to the block core, along
with a 'blkdev_issue_discard()' helper function which issues such
requests.
The caller doesn't get to provide an end_io functio, since
blkdev_issue_discard() will automatically split the request up into
multiple bios if appropriate. Neither does the function wait for
completion -- it's expected that callers won't care about when, or even
_if_, the request completes. It's only a hint to the device anyway. By
definition, the file system doesn't _care_ about these sectors any more.
[With feedback from OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> and
Jens Axboe <jens.axboe@oracle.com]
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'block/blk-barrier.c')
-rw-r--r-- | block/blk-barrier.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/block/blk-barrier.c b/block/blk-barrier.c index a09ead19f9c5..273121c0eb80 100644 --- a/block/blk-barrier.c +++ b/block/blk-barrier.c | |||
@@ -315,3 +315,72 @@ int blkdev_issue_flush(struct block_device *bdev, sector_t *error_sector) | |||
315 | return ret; | 315 | return ret; |
316 | } | 316 | } |
317 | EXPORT_SYMBOL(blkdev_issue_flush); | 317 | EXPORT_SYMBOL(blkdev_issue_flush); |
318 | |||
319 | static void blkdev_discard_end_io(struct bio *bio, int err) | ||
320 | { | ||
321 | if (err) { | ||
322 | if (err == -EOPNOTSUPP) | ||
323 | set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); | ||
324 | clear_bit(BIO_UPTODATE, &bio->bi_flags); | ||
325 | } | ||
326 | |||
327 | bio_put(bio); | ||
328 | } | ||
329 | |||
330 | /** | ||
331 | * blkdev_issue_discard - queue a discard | ||
332 | * @bdev: blockdev to issue discard for | ||
333 | * @sector: start sector | ||
334 | * @nr_sects: number of sectors to discard | ||
335 | * | ||
336 | * Description: | ||
337 | * Issue a discard request for the sectors in question. Does not wait. | ||
338 | */ | ||
339 | int blkdev_issue_discard(struct block_device *bdev, sector_t sector, | ||
340 | unsigned nr_sects) | ||
341 | { | ||
342 | struct request_queue *q; | ||
343 | struct bio *bio; | ||
344 | int ret = 0; | ||
345 | |||
346 | if (bdev->bd_disk == NULL) | ||
347 | return -ENXIO; | ||
348 | |||
349 | q = bdev_get_queue(bdev); | ||
350 | if (!q) | ||
351 | return -ENXIO; | ||
352 | |||
353 | if (!q->prepare_discard_fn) | ||
354 | return -EOPNOTSUPP; | ||
355 | |||
356 | while (nr_sects && !ret) { | ||
357 | bio = bio_alloc(GFP_KERNEL, 0); | ||
358 | if (!bio) | ||
359 | return -ENOMEM; | ||
360 | |||
361 | bio->bi_end_io = blkdev_discard_end_io; | ||
362 | bio->bi_bdev = bdev; | ||
363 | |||
364 | bio->bi_sector = sector; | ||
365 | |||
366 | if (nr_sects > q->max_hw_sectors) { | ||
367 | bio->bi_size = q->max_hw_sectors << 9; | ||
368 | nr_sects -= q->max_hw_sectors; | ||
369 | sector += q->max_hw_sectors; | ||
370 | } else { | ||
371 | bio->bi_size = nr_sects << 9; | ||
372 | nr_sects = 0; | ||
373 | } | ||
374 | bio_get(bio); | ||
375 | submit_bio(WRITE_DISCARD, bio); | ||
376 | |||
377 | /* Check if it failed immediately */ | ||
378 | if (bio_flagged(bio, BIO_EOPNOTSUPP)) | ||
379 | ret = -EOPNOTSUPP; | ||
380 | else if (!bio_flagged(bio, BIO_UPTODATE)) | ||
381 | ret = -EIO; | ||
382 | bio_put(bio); | ||
383 | } | ||
384 | return ret; | ||
385 | } | ||
386 | EXPORT_SYMBOL(blkdev_issue_discard); | ||