diff options
author | Martin K. Petersen <martin.petersen@oracle.com> | 2009-11-10 05:50:21 -0500 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-11-10 05:50:21 -0500 |
commit | 86b37281411cf1e9bc0a6b5406c45edb7bd9ea5d (patch) | |
tree | 729db57dd52054af1bc16b4afb131093dfc9d255 | |
parent | cf7c25cf91f632a3528669fc0876e1fc8355ff9b (diff) |
block: Expose discard granularity
While SSDs track block usage on a per-sector basis, RAID arrays often
have allocation blocks that are bigger. Allow the discard granularity
and alignment to be set and teach the topology stacking logic how to
handle them.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r-- | block/blk-settings.c | 46 | ||||
-rw-r--r-- | block/blk-sysfs.c | 22 | ||||
-rw-r--r-- | block/genhd.c | 12 | ||||
-rw-r--r-- | fs/partitions/check.c | 12 | ||||
-rw-r--r-- | include/linux/blkdev.h | 18 | ||||
-rw-r--r-- | include/linux/genhd.h | 1 |
6 files changed, 101 insertions, 10 deletions
diff --git a/block/blk-settings.c b/block/blk-settings.c index 66d4aa8799b7..7f986cafacd5 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c | |||
@@ -96,7 +96,10 @@ void blk_set_default_limits(struct queue_limits *lim) | |||
96 | lim->max_segment_size = MAX_SEGMENT_SIZE; | 96 | lim->max_segment_size = MAX_SEGMENT_SIZE; |
97 | lim->max_sectors = BLK_DEF_MAX_SECTORS; | 97 | lim->max_sectors = BLK_DEF_MAX_SECTORS; |
98 | lim->max_hw_sectors = INT_MAX; | 98 | lim->max_hw_sectors = INT_MAX; |
99 | lim->max_discard_sectors = SAFE_MAX_SECTORS; | 99 | lim->max_discard_sectors = 0; |
100 | lim->discard_granularity = 0; | ||
101 | lim->discard_alignment = 0; | ||
102 | lim->discard_misaligned = 0; | ||
100 | lim->logical_block_size = lim->physical_block_size = lim->io_min = 512; | 103 | lim->logical_block_size = lim->physical_block_size = lim->io_min = 512; |
101 | lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT); | 104 | lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT); |
102 | lim->alignment_offset = 0; | 105 | lim->alignment_offset = 0; |
@@ -488,6 +491,16 @@ void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b) | |||
488 | } | 491 | } |
489 | EXPORT_SYMBOL(blk_queue_stack_limits); | 492 | EXPORT_SYMBOL(blk_queue_stack_limits); |
490 | 493 | ||
494 | static unsigned int lcm(unsigned int a, unsigned int b) | ||
495 | { | ||
496 | if (a && b) | ||
497 | return (a * b) / gcd(a, b); | ||
498 | else if (b) | ||
499 | return b; | ||
500 | |||
501 | return a; | ||
502 | } | ||
503 | |||
491 | /** | 504 | /** |
492 | * blk_stack_limits - adjust queue_limits for stacked devices | 505 | * blk_stack_limits - adjust queue_limits for stacked devices |
493 | * @t: the stacking driver limits (top) | 506 | * @t: the stacking driver limits (top) |
@@ -502,6 +515,10 @@ EXPORT_SYMBOL(blk_queue_stack_limits); | |||
502 | int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, | 515 | int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, |
503 | sector_t offset) | 516 | sector_t offset) |
504 | { | 517 | { |
518 | int ret; | ||
519 | |||
520 | ret = 0; | ||
521 | |||
505 | t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors); | 522 | t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors); |
506 | t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors); | 523 | t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors); |
507 | t->bounce_pfn = min_not_zero(t->bounce_pfn, b->bounce_pfn); | 524 | t->bounce_pfn = min_not_zero(t->bounce_pfn, b->bounce_pfn); |
@@ -531,7 +548,13 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, | |||
531 | if (offset && | 548 | if (offset && |
532 | (offset & (b->physical_block_size - 1)) != b->alignment_offset) { | 549 | (offset & (b->physical_block_size - 1)) != b->alignment_offset) { |
533 | t->misaligned = 1; | 550 | t->misaligned = 1; |
534 | return -1; | 551 | ret = -1; |
552 | } | ||
553 | |||
554 | if (offset && | ||
555 | (offset & (b->discard_granularity - 1)) != b->discard_alignment) { | ||
556 | t->discard_misaligned = 1; | ||
557 | ret = -1; | ||
535 | } | 558 | } |
536 | 559 | ||
537 | /* If top has no alignment offset, inherit from bottom */ | 560 | /* If top has no alignment offset, inherit from bottom */ |
@@ -539,23 +562,26 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, | |||
539 | t->alignment_offset = | 562 | t->alignment_offset = |
540 | b->alignment_offset & (b->physical_block_size - 1); | 563 | b->alignment_offset & (b->physical_block_size - 1); |
541 | 564 | ||
565 | if (!t->discard_alignment) | ||
566 | t->discard_alignment = | ||
567 | b->discard_alignment & (b->discard_granularity - 1); | ||
568 | |||
542 | /* Top device aligned on logical block boundary? */ | 569 | /* Top device aligned on logical block boundary? */ |
543 | if (t->alignment_offset & (t->logical_block_size - 1)) { | 570 | if (t->alignment_offset & (t->logical_block_size - 1)) { |
544 | t->misaligned = 1; | 571 | t->misaligned = 1; |
545 | return -1; | 572 | ret = -1; |
546 | } | 573 | } |
547 | 574 | ||
548 | /* Find lcm() of optimal I/O size */ | 575 | /* Find lcm() of optimal I/O size and granularity */ |
549 | if (t->io_opt && b->io_opt) | 576 | t->io_opt = lcm(t->io_opt, b->io_opt); |
550 | t->io_opt = (t->io_opt * b->io_opt) / gcd(t->io_opt, b->io_opt); | 577 | t->discard_granularity = lcm(t->discard_granularity, |
551 | else if (b->io_opt) | 578 | b->discard_granularity); |
552 | t->io_opt = b->io_opt; | ||
553 | 579 | ||
554 | /* Verify that optimal I/O size is a multiple of io_min */ | 580 | /* Verify that optimal I/O size is a multiple of io_min */ |
555 | if (t->io_min && t->io_opt % t->io_min) | 581 | if (t->io_min && t->io_opt % t->io_min) |
556 | return -1; | 582 | ret = -1; |
557 | 583 | ||
558 | return 0; | 584 | return ret; |
559 | } | 585 | } |
560 | EXPORT_SYMBOL(blk_stack_limits); | 586 | EXPORT_SYMBOL(blk_stack_limits); |
561 | 587 | ||
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 8a6d81afb284..3147145edc15 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c | |||
@@ -126,6 +126,16 @@ static ssize_t queue_io_opt_show(struct request_queue *q, char *page) | |||
126 | return queue_var_show(queue_io_opt(q), page); | 126 | return queue_var_show(queue_io_opt(q), page); |
127 | } | 127 | } |
128 | 128 | ||
129 | static ssize_t queue_discard_granularity_show(struct request_queue *q, char *page) | ||
130 | { | ||
131 | return queue_var_show(q->limits.discard_granularity, page); | ||
132 | } | ||
133 | |||
134 | static ssize_t queue_discard_max_show(struct request_queue *q, char *page) | ||
135 | { | ||
136 | return queue_var_show(q->limits.max_discard_sectors << 9, page); | ||
137 | } | ||
138 | |||
129 | static ssize_t | 139 | static ssize_t |
130 | queue_max_sectors_store(struct request_queue *q, const char *page, size_t count) | 140 | queue_max_sectors_store(struct request_queue *q, const char *page, size_t count) |
131 | { | 141 | { |
@@ -293,6 +303,16 @@ static struct queue_sysfs_entry queue_io_opt_entry = { | |||
293 | .show = queue_io_opt_show, | 303 | .show = queue_io_opt_show, |
294 | }; | 304 | }; |
295 | 305 | ||
306 | static struct queue_sysfs_entry queue_discard_granularity_entry = { | ||
307 | .attr = {.name = "discard_granularity", .mode = S_IRUGO }, | ||
308 | .show = queue_discard_granularity_show, | ||
309 | }; | ||
310 | |||
311 | static struct queue_sysfs_entry queue_discard_max_entry = { | ||
312 | .attr = {.name = "discard_max_bytes", .mode = S_IRUGO }, | ||
313 | .show = queue_discard_max_show, | ||
314 | }; | ||
315 | |||
296 | static struct queue_sysfs_entry queue_nonrot_entry = { | 316 | static struct queue_sysfs_entry queue_nonrot_entry = { |
297 | .attr = {.name = "rotational", .mode = S_IRUGO | S_IWUSR }, | 317 | .attr = {.name = "rotational", .mode = S_IRUGO | S_IWUSR }, |
298 | .show = queue_nonrot_show, | 318 | .show = queue_nonrot_show, |
@@ -328,6 +348,8 @@ static struct attribute *default_attrs[] = { | |||
328 | &queue_physical_block_size_entry.attr, | 348 | &queue_physical_block_size_entry.attr, |
329 | &queue_io_min_entry.attr, | 349 | &queue_io_min_entry.attr, |
330 | &queue_io_opt_entry.attr, | 350 | &queue_io_opt_entry.attr, |
351 | &queue_discard_granularity_entry.attr, | ||
352 | &queue_discard_max_entry.attr, | ||
331 | &queue_nonrot_entry.attr, | 353 | &queue_nonrot_entry.attr, |
332 | &queue_nomerges_entry.attr, | 354 | &queue_nomerges_entry.attr, |
333 | &queue_rq_affinity_entry.attr, | 355 | &queue_rq_affinity_entry.attr, |
diff --git a/block/genhd.c b/block/genhd.c index 517e4332cb37..b11a4ad7d571 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -861,12 +861,23 @@ static ssize_t disk_alignment_offset_show(struct device *dev, | |||
861 | return sprintf(buf, "%d\n", queue_alignment_offset(disk->queue)); | 861 | return sprintf(buf, "%d\n", queue_alignment_offset(disk->queue)); |
862 | } | 862 | } |
863 | 863 | ||
864 | static ssize_t disk_discard_alignment_show(struct device *dev, | ||
865 | struct device_attribute *attr, | ||
866 | char *buf) | ||
867 | { | ||
868 | struct gendisk *disk = dev_to_disk(dev); | ||
869 | |||
870 | return sprintf(buf, "%u\n", queue_discard_alignment(disk->queue)); | ||
871 | } | ||
872 | |||
864 | static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); | 873 | static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); |
865 | static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); | 874 | static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); |
866 | static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); | 875 | static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); |
867 | static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); | 876 | static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); |
868 | static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); | 877 | static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); |
869 | static DEVICE_ATTR(alignment_offset, S_IRUGO, disk_alignment_offset_show, NULL); | 878 | static DEVICE_ATTR(alignment_offset, S_IRUGO, disk_alignment_offset_show, NULL); |
879 | static DEVICE_ATTR(discard_alignment, S_IRUGO, disk_discard_alignment_show, | ||
880 | NULL); | ||
870 | static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); | 881 | static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); |
871 | static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); | 882 | static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); |
872 | static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); | 883 | static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); |
@@ -887,6 +898,7 @@ static struct attribute *disk_attrs[] = { | |||
887 | &dev_attr_ro.attr, | 898 | &dev_attr_ro.attr, |
888 | &dev_attr_size.attr, | 899 | &dev_attr_size.attr, |
889 | &dev_attr_alignment_offset.attr, | 900 | &dev_attr_alignment_offset.attr, |
901 | &dev_attr_discard_alignment.attr, | ||
890 | &dev_attr_capability.attr, | 902 | &dev_attr_capability.attr, |
891 | &dev_attr_stat.attr, | 903 | &dev_attr_stat.attr, |
892 | &dev_attr_inflight.attr, | 904 | &dev_attr_inflight.attr, |
diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 7b685e10cbad..64bc8998ac9a 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c | |||
@@ -226,6 +226,13 @@ ssize_t part_alignment_offset_show(struct device *dev, | |||
226 | return sprintf(buf, "%llu\n", (unsigned long long)p->alignment_offset); | 226 | return sprintf(buf, "%llu\n", (unsigned long long)p->alignment_offset); |
227 | } | 227 | } |
228 | 228 | ||
229 | ssize_t part_discard_alignment_show(struct device *dev, | ||
230 | struct device_attribute *attr, char *buf) | ||
231 | { | ||
232 | struct hd_struct *p = dev_to_part(dev); | ||
233 | return sprintf(buf, "%u\n", p->discard_alignment); | ||
234 | } | ||
235 | |||
229 | ssize_t part_stat_show(struct device *dev, | 236 | ssize_t part_stat_show(struct device *dev, |
230 | struct device_attribute *attr, char *buf) | 237 | struct device_attribute *attr, char *buf) |
231 | { | 238 | { |
@@ -288,6 +295,8 @@ static DEVICE_ATTR(partition, S_IRUGO, part_partition_show, NULL); | |||
288 | static DEVICE_ATTR(start, S_IRUGO, part_start_show, NULL); | 295 | static DEVICE_ATTR(start, S_IRUGO, part_start_show, NULL); |
289 | static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); | 296 | static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); |
290 | static DEVICE_ATTR(alignment_offset, S_IRUGO, part_alignment_offset_show, NULL); | 297 | static DEVICE_ATTR(alignment_offset, S_IRUGO, part_alignment_offset_show, NULL); |
298 | static DEVICE_ATTR(discard_alignment, S_IRUGO, part_discard_alignment_show, | ||
299 | NULL); | ||
291 | static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); | 300 | static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); |
292 | static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); | 301 | static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); |
293 | #ifdef CONFIG_FAIL_MAKE_REQUEST | 302 | #ifdef CONFIG_FAIL_MAKE_REQUEST |
@@ -300,6 +309,7 @@ static struct attribute *part_attrs[] = { | |||
300 | &dev_attr_start.attr, | 309 | &dev_attr_start.attr, |
301 | &dev_attr_size.attr, | 310 | &dev_attr_size.attr, |
302 | &dev_attr_alignment_offset.attr, | 311 | &dev_attr_alignment_offset.attr, |
312 | &dev_attr_discard_alignment.attr, | ||
303 | &dev_attr_stat.attr, | 313 | &dev_attr_stat.attr, |
304 | &dev_attr_inflight.attr, | 314 | &dev_attr_inflight.attr, |
305 | #ifdef CONFIG_FAIL_MAKE_REQUEST | 315 | #ifdef CONFIG_FAIL_MAKE_REQUEST |
@@ -403,6 +413,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, | |||
403 | 413 | ||
404 | p->start_sect = start; | 414 | p->start_sect = start; |
405 | p->alignment_offset = queue_sector_alignment_offset(disk->queue, start); | 415 | p->alignment_offset = queue_sector_alignment_offset(disk->queue, start); |
416 | p->discard_alignment = queue_sector_discard_alignment(disk->queue, | ||
417 | start); | ||
406 | p->nr_sects = len; | 418 | p->nr_sects = len; |
407 | p->partno = partno; | 419 | p->partno = partno; |
408 | p->policy = get_disk_ro(disk); | 420 | p->policy = get_disk_ro(disk); |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 39c601f783a0..1cc02972fbe2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
@@ -312,12 +312,15 @@ struct queue_limits { | |||
312 | unsigned int io_min; | 312 | unsigned int io_min; |
313 | unsigned int io_opt; | 313 | unsigned int io_opt; |
314 | unsigned int max_discard_sectors; | 314 | unsigned int max_discard_sectors; |
315 | unsigned int discard_granularity; | ||
316 | unsigned int discard_alignment; | ||
315 | 317 | ||
316 | unsigned short logical_block_size; | 318 | unsigned short logical_block_size; |
317 | unsigned short max_hw_segments; | 319 | unsigned short max_hw_segments; |
318 | unsigned short max_phys_segments; | 320 | unsigned short max_phys_segments; |
319 | 321 | ||
320 | unsigned char misaligned; | 322 | unsigned char misaligned; |
323 | unsigned char discard_misaligned; | ||
321 | unsigned char no_cluster; | 324 | unsigned char no_cluster; |
322 | }; | 325 | }; |
323 | 326 | ||
@@ -1121,6 +1124,21 @@ static inline int bdev_alignment_offset(struct block_device *bdev) | |||
1121 | return q->limits.alignment_offset; | 1124 | return q->limits.alignment_offset; |
1122 | } | 1125 | } |
1123 | 1126 | ||
1127 | static inline int queue_discard_alignment(struct request_queue *q) | ||
1128 | { | ||
1129 | if (q->limits.discard_misaligned) | ||
1130 | return -1; | ||
1131 | |||
1132 | return q->limits.discard_alignment; | ||
1133 | } | ||
1134 | |||
1135 | static inline int queue_sector_discard_alignment(struct request_queue *q, | ||
1136 | sector_t sector) | ||
1137 | { | ||
1138 | return ((sector << 9) - q->limits.discard_alignment) | ||
1139 | & (q->limits.discard_granularity - 1); | ||
1140 | } | ||
1141 | |||
1124 | static inline int queue_dma_alignment(struct request_queue *q) | 1142 | static inline int queue_dma_alignment(struct request_queue *q) |
1125 | { | 1143 | { |
1126 | return q ? q->dma_alignment : 511; | 1144 | return q ? q->dma_alignment : 511; |
diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 297df45ffd0a..c6c0c41af35f 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h | |||
@@ -91,6 +91,7 @@ struct hd_struct { | |||
91 | sector_t start_sect; | 91 | sector_t start_sect; |
92 | sector_t nr_sects; | 92 | sector_t nr_sects; |
93 | sector_t alignment_offset; | 93 | sector_t alignment_offset; |
94 | unsigned int discard_alignment; | ||
94 | struct device __dev; | 95 | struct device __dev; |
95 | struct kobject *holder_dir; | 96 | struct kobject *holder_dir; |
96 | int policy, partno; | 97 | int policy, partno; |