aboutsummaryrefslogtreecommitdiffstats
path: root/block/blk-barrier.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/blk-barrier.c')
-rw-r--r--block/blk-barrier.c45
1 files changed, 36 insertions, 9 deletions
diff --git a/block/blk-barrier.c b/block/blk-barrier.c
index 6593ab39cfe9..8873b9b439ff 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,30 +373,50 @@ 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) 388 unsigned int max_discard_sectors =
386 return -ENOMEM; 389 min(q->limits.max_discard_sectors, UINT_MAX >> 9);
387 390
391 bio = bio_alloc(gfp_mask, 1);
392 if (!bio)
393 goto out;
394 bio->bi_sector = sector;
388 bio->bi_end_io = blkdev_discard_end_io; 395 bio->bi_end_io = blkdev_discard_end_io;
389 bio->bi_bdev = bdev; 396 bio->bi_bdev = bdev;
390 if (flags & DISCARD_FL_WAIT) 397 if (flags & DISCARD_FL_WAIT)
391 bio->bi_private = &wait; 398 bio->bi_private = &wait;
392 399
393 bio->bi_sector = sector; 400 /*
401 * Add a zeroed one-sector payload as that's what
402 * our current implementations need. If we'll ever need
403 * more the interface will need revisiting.
404 */
405 page = alloc_page(GFP_KERNEL | __GFP_ZERO);
406 if (!page)
407 goto out_free_bio;
408 if (bio_add_pc_page(q, bio, page, sector_size, 0) < sector_size)
409 goto out_free_page;
394 410
395 if (nr_sects > queue_max_hw_sectors(q)) { 411 /*
396 bio->bi_size = queue_max_hw_sectors(q) << 9; 412 * And override the bio size - the way discard works we
397 nr_sects -= queue_max_hw_sectors(q); 413 * touch many more blocks on disk than the actual payload
398 sector += queue_max_hw_sectors(q); 414 * length.
415 */
416 if (nr_sects > max_discard_sectors) {
417 bio->bi_size = max_discard_sectors << 9;
418 nr_sects -= max_discard_sectors;
419 sector += max_discard_sectors;
399 } else { 420 } else {
400 bio->bi_size = nr_sects << 9; 421 bio->bi_size = nr_sects << 9;
401 nr_sects = 0; 422 nr_sects = 0;
@@ -414,5 +435,11 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
414 bio_put(bio); 435 bio_put(bio);
415 } 436 }
416 return ret; 437 return ret;
438out_free_page:
439 __free_page(page);
440out_free_bio:
441 bio_put(bio);
442out:
443 return -ENOMEM;
417} 444}
418EXPORT_SYMBOL(blkdev_issue_discard); 445EXPORT_SYMBOL(blkdev_issue_discard);