@@ -157,6 +157,42 @@ static int __blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
return ret;
}
+/*
+ * Return the largest number that is less than or equal to @s and for which
+ * the remainder of the division by @granularity is @alignment.
+ */
+static sector_t round_sect_down(sector_t s, u32 granularity, u32 alignment)
+{
+ sector_t tmp = s, res = s;
+ u32 remainder;
+
+ remainder = sector_div(tmp, granularity);
+ if (remainder == alignment)
+ return res;
+ res -= remainder - alignment;
+ if (remainder < alignment)
+ res -= granularity;
+ return min(res, s);
+}
+
+/*
+ * Return the smallest number that is greater than or equal to @s and for which
+ * the remainder of the division by @granularity is @alignment.
+ */
+static sector_t round_sect_up(sector_t s, u32 granularity, u32 alignment)
+{
+ sector_t tmp = s, res = s;
+ u32 remainder;
+
+ remainder = sector_div(tmp, granularity);
+ if (remainder == alignment)
+ return res;
+ res += alignment - remainder;
+ if (alignment < remainder)
+ res += granularity;
+ return max(res, s);
+}
+
/**
* blkdev_issue_discard - queue a discard
* @bdev: blockdev to issue discard for
@@ -174,8 +210,9 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
DECLARE_COMPLETION_ONSTACK(wait);
struct request_queue *q = bdev_get_queue(bdev);
int type = REQ_WRITE | REQ_DISCARD;
- unsigned int granularity;
- int alignment;
+ unsigned int granularity, req_sects;
+ sector_t end_sect;
+ int alignment, res;
struct bio_batch bb;
struct bio *bio;
int ret = 0;
@@ -202,10 +239,19 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
bb.wait = &wait;
blk_start_plug(&plug);
+ end_sect = round_sect_up(sector, granularity, alignment);
+ if (sector < end_sect) {
+ req_sects = min(end_sect - sector, nr_sects);
+ res = __blkdev_issue_zeroout(bdev, sector, req_sects, gfp_mask);
+ if (res) {
+ bb.error = res;
+ req_sects = nr_sects;
+ end_sect = sector + nr_sects;
+ }
+ nr_sects -= req_sects;
+ sector = end_sect;
+ }
while (nr_sects) {
- unsigned int req_sects;
- sector_t end_sect, tmp;
-
bio = bio_alloc(gfp_mask, 1);
if (!bio) {
ret = -ENOMEM;
@@ -219,15 +265,12 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
* If splitting a request, and the next starting sector would be
* misaligned, stop the discard at the previous aligned sector.
*/
- end_sect = sector + req_sects;
- tmp = end_sect;
- if (req_sects < nr_sects &&
- sector_div(tmp, granularity) != alignment) {
- end_sect = end_sect - alignment;
- sector_div(end_sect, granularity);
- end_sect = end_sect * granularity + alignment;
- req_sects = end_sect - sector;
- }
+ end_sect = round_sect_down(sector + req_sects, granularity,
+ alignment);
+ if (end_sect <= sector)
+ break;
+
+ req_sects = end_sect - sector;
bio->bi_iter.bi_sector = sector;
bio->bi_end_io = bio_batch_end_io;
@@ -249,6 +292,9 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
*/
cond_resched();
}
+ res = __blkdev_issue_zeroout(bdev, sector, nr_sects, gfp_mask);
+ if (res)
+ bb.error = res;
blk_finish_plug(&plug);
/* Wait for bios in-flight */
If the start or the end of a discard range does not satisfy the alignment requirements of a block driver, call __blkdev_issue_zeroout() for the first and/or last part instead of using REQ_DISCARD. Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com> Cc: Jan Kara <jack@suse.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Martin K. Petersen <martin.petersen@oracle.com> Cc: Ming Lin <ming.l@ssi.samsung.com> Cc: Mike Snitzer <snitzer@redhat.com> Cc: stable <stable@vger.kernel.org> --- block/blk-lib.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 14 deletions(-)