Commit 8dd2cb7e880d2f77fba53b523c99133ad5054cfd

Authored by Shaohua Li
Committed by Jens Axboe
1 parent 75274551c8

block: discard granularity might not be power of 2

In MD raid case, discard granularity might not be power of 2, for example, a
4-disk raid5 has 3*chunk_size discard granularity. Correct the calculation for
such cases.

Reported-by: Neil Brown <neilb@suse.de>
Signed-off-by: Shaohua Li <shli@fusionio.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

Showing 3 changed files with 20 additions and 16 deletions Side-by-side Diff

... ... @@ -43,8 +43,8 @@
43 43 DECLARE_COMPLETION_ONSTACK(wait);
44 44 struct request_queue *q = bdev_get_queue(bdev);
45 45 int type = REQ_WRITE | REQ_DISCARD;
46   - unsigned int max_discard_sectors;
47   - unsigned int granularity, alignment, mask;
  46 + sector_t max_discard_sectors;
  47 + sector_t granularity, alignment;
48 48 struct bio_batch bb;
49 49 struct bio *bio;
50 50 int ret = 0;
51 51  
... ... @@ -57,15 +57,16 @@
57 57  
58 58 /* Zero-sector (unknown) and one-sector granularities are the same. */
59 59 granularity = max(q->limits.discard_granularity >> 9, 1U);
60   - mask = granularity - 1;
61   - alignment = (bdev_discard_alignment(bdev) >> 9) & mask;
  60 + alignment = bdev_discard_alignment(bdev) >> 9;
  61 + alignment = sector_div(alignment, granularity);
62 62  
63 63 /*
64 64 * Ensure that max_discard_sectors is of the proper
65 65 * granularity, so that requests stay aligned after a split.
66 66 */
67 67 max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9);
68   - max_discard_sectors = round_down(max_discard_sectors, granularity);
  68 + sector_div(max_discard_sectors, granularity);
  69 + max_discard_sectors *= granularity;
69 70 if (unlikely(!max_discard_sectors)) {
70 71 /* Avoid infinite loop below. Being cautious never hurts. */
71 72 return -EOPNOTSUPP;
... ... @@ -83,7 +84,7 @@
83 84  
84 85 while (nr_sects) {
85 86 unsigned int req_sects;
86   - sector_t end_sect;
  87 + sector_t end_sect, tmp;
87 88  
88 89 bio = bio_alloc(gfp_mask, 1);
89 90 if (!bio) {
... ... @@ -98,10 +99,12 @@
98 99 * misaligned, stop the discard at the previous aligned sector.
99 100 */
100 101 end_sect = sector + req_sects;
101   - if (req_sects < nr_sects && (end_sect & mask) != alignment) {
102   - end_sect =
103   - round_down(end_sect - alignment, granularity)
104   - + alignment;
  102 + tmp = end_sect;
  103 + if (req_sects < nr_sects &&
  104 + sector_div(tmp, granularity) != alignment) {
  105 + end_sect = end_sect - alignment;
  106 + sector_div(end_sect, granularity);
  107 + end_sect = end_sect * granularity + alignment;
105 108 req_sects = end_sect - sector;
106 109 }
107 110  
block/blk-settings.c
... ... @@ -611,7 +611,7 @@
611 611 bottom = b->discard_granularity + alignment;
612 612  
613 613 /* Verify that top and bottom intervals line up */
614   - if (max(top, bottom) & (min(top, bottom) - 1))
  614 + if ((max(top, bottom) % min(top, bottom)) != 0)
615 615 t->discard_misaligned = 1;
616 616 }
617 617  
... ... @@ -619,8 +619,8 @@
619 619 b->max_discard_sectors);
620 620 t->discard_granularity = max(t->discard_granularity,
621 621 b->discard_granularity);
622   - t->discard_alignment = lcm(t->discard_alignment, alignment) &
623   - (t->discard_granularity - 1);
  622 + t->discard_alignment = lcm(t->discard_alignment, alignment) %
  623 + t->discard_granularity;
624 624 }
625 625  
626 626 return ret;
include/linux/blkdev.h
... ... @@ -1188,13 +1188,14 @@
1188 1188  
1189 1189 static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector_t sector)
1190 1190 {
1191   - unsigned int alignment = (sector << 9) & (lim->discard_granularity - 1);
  1191 + sector_t alignment = sector << 9;
  1192 + alignment = sector_div(alignment, lim->discard_granularity);
1192 1193  
1193 1194 if (!lim->max_discard_sectors)
1194 1195 return 0;
1195 1196  
1196   - return (lim->discard_granularity + lim->discard_alignment - alignment)
1197   - & (lim->discard_granularity - 1);
  1197 + alignment = lim->discard_granularity + lim->discard_alignment - alignment;
  1198 + return sector_div(alignment, lim->discard_granularity);
1198 1199 }
1199 1200  
1200 1201 static inline int bdev_discard_alignment(struct block_device *bdev)