Commit ca80650c authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe

block: allow large discard requests

Currently we set the bio size to the byte equivalent of the blocks to
be trimmed when submitting the initial DISCARD ioctl.  That means it
is subject to the max_hw_sectors limitation of the HBA which is
much lower than the size of a DISCARD request we can support.
Add a separate max_discard_sectors tunable to limit the size for discard
requests.

We limit the max discard request size in bytes to 32bit as that is the
limit for bio->bi_size.  This could be much larger if we had a way to pass
that information through the block layer.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent 1122a26f
...@@ -385,6 +385,8 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, ...@@ -385,6 +385,8 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
while (nr_sects && !ret) { while (nr_sects && !ret) {
unsigned int sector_size = q->limits.logical_block_size; unsigned int sector_size = q->limits.logical_block_size;
unsigned int max_discard_sectors =
min(q->limits.max_discard_sectors, UINT_MAX >> 9);
bio = bio_alloc(gfp_mask, 1); bio = bio_alloc(gfp_mask, 1);
if (!bio) if (!bio)
...@@ -411,10 +413,10 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, ...@@ -411,10 +413,10 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
* touch many more blocks on disk than the actual payload * touch many more blocks on disk than the actual payload
* length. * length.
*/ */
if (nr_sects > queue_max_hw_sectors(q)) { if (nr_sects > max_discard_sectors) {
bio->bi_size = queue_max_hw_sectors(q) << 9; bio->bi_size = max_discard_sectors << 9;
nr_sects -= queue_max_hw_sectors(q); nr_sects -= max_discard_sectors;
sector += queue_max_hw_sectors(q); sector += max_discard_sectors;
} else { } else {
bio->bi_size = nr_sects << 9; bio->bi_size = nr_sects << 9;
nr_sects = 0; nr_sects = 0;
......
...@@ -1436,7 +1436,8 @@ static inline void __generic_make_request(struct bio *bio) ...@@ -1436,7 +1436,8 @@ static inline void __generic_make_request(struct bio *bio)
goto end_io; goto end_io;
} }
if (unlikely(nr_sectors > queue_max_hw_sectors(q))) { if (unlikely(!bio_rw_flagged(bio, BIO_RW_DISCARD) &&
nr_sectors > queue_max_hw_sectors(q))) {
printk(KERN_ERR "bio too big device %s (%u > %u)\n", printk(KERN_ERR "bio too big device %s (%u > %u)\n",
bdevname(bio->bi_bdev, b), bdevname(bio->bi_bdev, b),
bio_sectors(bio), bio_sectors(bio),
......
...@@ -96,6 +96,7 @@ void blk_set_default_limits(struct queue_limits *lim) ...@@ -96,6 +96,7 @@ void blk_set_default_limits(struct queue_limits *lim)
lim->max_segment_size = MAX_SEGMENT_SIZE; lim->max_segment_size = MAX_SEGMENT_SIZE;
lim->max_sectors = BLK_DEF_MAX_SECTORS; lim->max_sectors = BLK_DEF_MAX_SECTORS;
lim->max_hw_sectors = INT_MAX; lim->max_hw_sectors = INT_MAX;
lim->max_discard_sectors = SAFE_MAX_SECTORS;
lim->logical_block_size = lim->physical_block_size = lim->io_min = 512; lim->logical_block_size = lim->physical_block_size = lim->io_min = 512;
lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT); lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT);
lim->alignment_offset = 0; lim->alignment_offset = 0;
...@@ -238,6 +239,18 @@ void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_sectors) ...@@ -238,6 +239,18 @@ void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_sectors)
} }
EXPORT_SYMBOL(blk_queue_max_hw_sectors); EXPORT_SYMBOL(blk_queue_max_hw_sectors);
/**
* blk_queue_max_discard_sectors - set max sectors for a single discard
* @q: the request queue for the device
* @max_discard: maximum number of sectors to discard
**/
void blk_queue_max_discard_sectors(struct request_queue *q,
unsigned int max_discard_sectors)
{
q->limits.max_discard_sectors = max_discard_sectors;
}
EXPORT_SYMBOL(blk_queue_max_discard_sectors);
/** /**
* blk_queue_max_phys_segments - set max phys segments for a request for this queue * blk_queue_max_phys_segments - set max phys segments for a request for this queue
* @q: the request queue for the device * @q: the request queue for the device
......
...@@ -311,6 +311,7 @@ struct queue_limits { ...@@ -311,6 +311,7 @@ struct queue_limits {
unsigned int alignment_offset; unsigned int alignment_offset;
unsigned int io_min; unsigned int io_min;
unsigned int io_opt; unsigned int io_opt;
unsigned int max_discard_sectors;
unsigned short logical_block_size; unsigned short logical_block_size;
unsigned short max_hw_segments; unsigned short max_hw_segments;
...@@ -928,6 +929,8 @@ extern void blk_queue_max_hw_sectors(struct request_queue *, unsigned int); ...@@ -928,6 +929,8 @@ extern void blk_queue_max_hw_sectors(struct request_queue *, unsigned int);
extern void blk_queue_max_phys_segments(struct request_queue *, unsigned short); extern void blk_queue_max_phys_segments(struct request_queue *, unsigned short);
extern void blk_queue_max_hw_segments(struct request_queue *, unsigned short); extern void blk_queue_max_hw_segments(struct request_queue *, unsigned short);
extern void blk_queue_max_segment_size(struct request_queue *, unsigned int); extern void blk_queue_max_segment_size(struct request_queue *, unsigned int);
extern void blk_queue_max_discard_sectors(struct request_queue *q,
unsigned int max_discard_sectors);
extern void blk_queue_logical_block_size(struct request_queue *, unsigned short); extern void blk_queue_logical_block_size(struct request_queue *, unsigned short);
extern void blk_queue_physical_block_size(struct request_queue *, unsigned short); extern void blk_queue_physical_block_size(struct request_queue *, unsigned short);
extern void blk_queue_alignment_offset(struct request_queue *q, extern void blk_queue_alignment_offset(struct request_queue *q,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment