diff options
author | Christoph Hellwig <hch@infradead.org> | 2009-09-30 07:52:12 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-10-01 15:15:46 -0400 |
commit | 1122a26f2abe4245ccdaed95ec23f63fe086b332 (patch) | |
tree | ad06f119f283cf8a6313681055e8132ba2851ddb /block | |
parent | 3bd0f0c763e497c8674b28e3df2732f48683dabd (diff) |
block: use normal I/O path for discard requests
prepare_discard_fn() was being called in a place where memory allocation
was effectively impossible. This makes it inappropriate for all but
the most trivial translations of Linux's DISCARD operation to the block
command set. Additionally adding a payload there makes the ownership
of the bio backing unclear as it's now allocated by the device driver
and not the submitter as usual.
It is replaced with QUEUE_FLAG_DISCARD which is used to indicate whether
the queue supports discard operations or not. blkdev_issue_discard now
allocates a one-page, sector-length payload which is the right thing
for the common ATA and SCSI implementations.
The mtd implementation of prepare_discard_fn() is replaced with simply
checking for the request being a discard.
Largely based on a previous patch from Matthew Wilcox <matthew@wil.cx>
which did the prepare_discard_fn but not the different payload allocation
yet.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'block')
-rw-r--r-- | block/blk-barrier.c | 35 | ||||
-rw-r--r-- | block/blk-core.c | 3 | ||||
-rw-r--r-- | block/blk-settings.c | 17 |
3 files changed, 31 insertions, 24 deletions
diff --git a/block/blk-barrier.c b/block/blk-barrier.c index 6593ab39cfe9..21f5025c3945 100644 --- a/block/blk-barrier.c +++ b/block/blk-barrier.c | |||
@@ -350,6 +350,7 @@ static void blkdev_discard_end_io(struct bio *bio, int err) | |||
350 | 350 | ||
351 | if (bio->bi_private) | 351 | if (bio->bi_private) |
352 | complete(bio->bi_private); | 352 | complete(bio->bi_private); |
353 | __free_page(bio_page(bio)); | ||
353 | 354 | ||
354 | bio_put(bio); | 355 | bio_put(bio); |
355 | } | 356 | } |
@@ -372,26 +373,44 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, | |||
372 | struct request_queue *q = bdev_get_queue(bdev); | 373 | struct request_queue *q = bdev_get_queue(bdev); |
373 | int type = flags & DISCARD_FL_BARRIER ? | 374 | int type = flags & DISCARD_FL_BARRIER ? |
374 | DISCARD_BARRIER : DISCARD_NOBARRIER; | 375 | DISCARD_BARRIER : DISCARD_NOBARRIER; |
376 | struct bio *bio; | ||
377 | struct page *page; | ||
375 | int ret = 0; | 378 | int ret = 0; |
376 | 379 | ||
377 | if (!q) | 380 | if (!q) |
378 | return -ENXIO; | 381 | return -ENXIO; |
379 | 382 | ||
380 | if (!q->prepare_discard_fn) | 383 | if (!blk_queue_discard(q)) |
381 | return -EOPNOTSUPP; | 384 | return -EOPNOTSUPP; |
382 | 385 | ||
383 | while (nr_sects && !ret) { | 386 | while (nr_sects && !ret) { |
384 | struct bio *bio = bio_alloc(gfp_mask, 0); | 387 | unsigned int sector_size = q->limits.logical_block_size; |
385 | if (!bio) | ||
386 | return -ENOMEM; | ||
387 | 388 | ||
389 | bio = bio_alloc(gfp_mask, 1); | ||
390 | if (!bio) | ||
391 | goto out; | ||
392 | bio->bi_sector = sector; | ||
388 | bio->bi_end_io = blkdev_discard_end_io; | 393 | bio->bi_end_io = blkdev_discard_end_io; |
389 | bio->bi_bdev = bdev; | 394 | bio->bi_bdev = bdev; |
390 | if (flags & DISCARD_FL_WAIT) | 395 | if (flags & DISCARD_FL_WAIT) |
391 | bio->bi_private = &wait; | 396 | bio->bi_private = &wait; |
392 | 397 | ||
393 | bio->bi_sector = sector; | 398 | /* |
399 | * Add a zeroed one-sector payload as that's what | ||
400 | * our current implementations need. If we'll ever need | ||
401 | * more the interface will need revisiting. | ||
402 | */ | ||
403 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); | ||
404 | if (!page) | ||
405 | goto out_free_bio; | ||
406 | if (bio_add_pc_page(q, bio, page, sector_size, 0) < sector_size) | ||
407 | goto out_free_page; | ||
394 | 408 | ||
409 | /* | ||
410 | * And override the bio size - the way discard works we | ||
411 | * touch many more blocks on disk than the actual payload | ||
412 | * length. | ||
413 | */ | ||
395 | if (nr_sects > queue_max_hw_sectors(q)) { | 414 | if (nr_sects > queue_max_hw_sectors(q)) { |
396 | bio->bi_size = queue_max_hw_sectors(q) << 9; | 415 | bio->bi_size = queue_max_hw_sectors(q) << 9; |
397 | nr_sects -= queue_max_hw_sectors(q); | 416 | nr_sects -= queue_max_hw_sectors(q); |
@@ -414,5 +433,11 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, | |||
414 | bio_put(bio); | 433 | bio_put(bio); |
415 | } | 434 | } |
416 | return ret; | 435 | return ret; |
436 | out_free_page: | ||
437 | __free_page(page); | ||
438 | out_free_bio: | ||
439 | bio_put(bio); | ||
440 | out: | ||
441 | return -ENOMEM; | ||
417 | } | 442 | } |
418 | EXPORT_SYMBOL(blkdev_issue_discard); | 443 | EXPORT_SYMBOL(blkdev_issue_discard); |
diff --git a/block/blk-core.c b/block/blk-core.c index 8135228e4b29..80a020dd1580 100644 --- a/block/blk-core.c +++ b/block/blk-core.c | |||
@@ -1124,7 +1124,6 @@ void init_request_from_bio(struct request *req, struct bio *bio) | |||
1124 | req->cmd_flags |= REQ_DISCARD; | 1124 | req->cmd_flags |= REQ_DISCARD; |
1125 | if (bio_rw_flagged(bio, BIO_RW_BARRIER)) | 1125 | if (bio_rw_flagged(bio, BIO_RW_BARRIER)) |
1126 | req->cmd_flags |= REQ_SOFTBARRIER; | 1126 | req->cmd_flags |= REQ_SOFTBARRIER; |
1127 | req->q->prepare_discard_fn(req->q, req); | ||
1128 | } else if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) | 1127 | } else if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) |
1129 | req->cmd_flags |= REQ_HARDBARRIER; | 1128 | req->cmd_flags |= REQ_HARDBARRIER; |
1130 | 1129 | ||
@@ -1470,7 +1469,7 @@ static inline void __generic_make_request(struct bio *bio) | |||
1470 | goto end_io; | 1469 | goto end_io; |
1471 | 1470 | ||
1472 | if (bio_rw_flagged(bio, BIO_RW_DISCARD) && | 1471 | if (bio_rw_flagged(bio, BIO_RW_DISCARD) && |
1473 | !q->prepare_discard_fn) { | 1472 | !blk_queue_discard(q)) { |
1474 | err = -EOPNOTSUPP; | 1473 | err = -EOPNOTSUPP; |
1475 | goto end_io; | 1474 | goto end_io; |
1476 | } | 1475 | } |
diff --git a/block/blk-settings.c b/block/blk-settings.c index eaf122ff5f16..d29498ef1eb5 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c | |||
@@ -34,23 +34,6 @@ void blk_queue_prep_rq(struct request_queue *q, prep_rq_fn *pfn) | |||
34 | EXPORT_SYMBOL(blk_queue_prep_rq); | 34 | EXPORT_SYMBOL(blk_queue_prep_rq); |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * blk_queue_set_discard - set a discard_sectors function for queue | ||
38 | * @q: queue | ||
39 | * @dfn: prepare_discard function | ||
40 | * | ||
41 | * It's possible for a queue to register a discard callback which is used | ||
42 | * to transform a discard request into the appropriate type for the | ||
43 | * hardware. If none is registered, then discard requests are failed | ||
44 | * with %EOPNOTSUPP. | ||
45 | * | ||
46 | */ | ||
47 | void blk_queue_set_discard(struct request_queue *q, prepare_discard_fn *dfn) | ||
48 | { | ||
49 | q->prepare_discard_fn = dfn; | ||
50 | } | ||
51 | EXPORT_SYMBOL(blk_queue_set_discard); | ||
52 | |||
53 | /** | ||
54 | * blk_queue_merge_bvec - set a merge_bvec function for queue | 37 | * blk_queue_merge_bvec - set a merge_bvec function for queue |
55 | * @q: queue | 38 | * @q: queue |
56 | * @mbfn: merge_bvec_fn | 39 | * @mbfn: merge_bvec_fn |