Commit 8dd2cb7e880d2f77fba53b523c99133ad5054cfd
Committed by
Jens Axboe
1 parent
75274551c8
Exists in
master
and in
20 other branches
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
block/blk-lib.c
... | ... | @@ -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) |