md: Subject: introduce get_priority_stripe() to improve raid456 write performance
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): 20080328164351.30557.patches@notabene
PATCH 1/1 md: Subject: introduce get_priority_stripe() to improve raid456 write performance
This patch is good for 2.6.26 (*not*) 2.6.25.
It substantially improves sequential write throughput for md/raid5.
Thanks,
NeilBrown
### Comments for Changeset
From: Dan Williams <[email protected]>
Improve write performance by preventing the delayed_list from dumping all its stripes onto the handle_list in one shot. Delayed stripes are now further delayed by being held on the 'hold_list'. The 'hold_list' is bypassed when:
* a STRIPE_IO_STARTED stripe is found at the head of 'handle_list'
* 'handle_list' is empty and i/o is being done to satisfy full stripe-width write requests
* 'bypass_count' is less than 'bypass_threshold'. By default the threshold is 1, i.e. every other stripe handled is a preread stripe provided the top two conditions are false.
Benchmark data:
System: 2x Xeon 5150, 4x SATA, mem=1GB
Baseline: 2.6.24-rc7
Configuration: mdadm --create /dev/md0 /dev/sd[b-e] -n 4 -l 5 --assume-clean
Test1: dd if=/dev/zero of=/dev/md0 bs=1024k count=2048
* patched: +33% (stripe_cache_size = 256), +25% (stripe_cache_size = 512)
Test2: tiobench --size 2048 --numruns 5 --block 4096 --block 131072 (XFS)
* patched: +13%
* patched + preread_bypass_threshold = 0: +37%
Changes since v1:
* reduce bypass_threshold from (chunk_size / sectors_per_chunk) to (1) and make it configurable. This defaults to fairness and modest performance gains out of the box.
Changes since v2:
* [[email protected]]: kill STRIPE_PRIO_HI and preread_needed as they are not necessary, the important change was clearing STRIPE_DELAYED in add_stripe_bio and this has been moved out to make_request for the hang fix.
* [[email protected]]: simplify get_priority_stripe
* [[email protected]]: reset the bypass_count when ->hold_list is sampled empty (+11%)
* [[email protected]]: decrement the bypass_count at the detection of stripes being naturally promoted off of hold_list +2%. Note, resetting bypass_count instead of decrementing on these events yields +4% but that is probably too aggressive.
Changes since v3:
* cosmetic fixups
### Diffstat output
./Documentation/md.txt | 6 ++ ./drivers/md/raid5.c | 122 +++++++++++++++++++++++++++++++++++++++---- ./include/linux/raid/raid5.h | 7 ++ 3 files changed, 125 insertions(+), 10 deletions(-)
Documentation/md.txt +6 -0
@@ -450,3 +450,9 @@ These currently include there are upper and lower limits (32768, 16). Default is 128. strip_cache_active (currently raid5 only) number of active entries in the stripe cache + preread_bypass_threshold (currently raid5 only) + number of times a stripe requiring preread will be bypassed by + a stripe that does not require preread. For fairness defaults + to 1. Setting this to 0 disables bypass accounting and + requires preread stripes to wait until all full-width stripe- + writes are complete. Valid values are 0 to stripe_cache_size.
drivers/md/raid5.c +112 -10
215 lines not on this page · raw message
include/linux/raid/raid5.h +7 -0
@@ -252,6 +252,8 @@ struct r6_state {#define STRIPE_EXPANDING 9#define STRIPE_EXPAND_SOURCE 10#define STRIPE_EXPAND_READY 11 +#define STRIPE_IO_STARTED 12/* do not count towards 'bypass_count' */ +#define STRIPE_FULL_WRITE 13/* all blocks are set to be overwritten */ /* * Operations flags (in issue order) */
@@ -316,12 +318,17 @@ struct raid5_private_data {int previous_raid_disks;struct list_head handle_list;/* stripes needing handling */ +struct list_head hold_list;/* preread ready stripes */ struct list_head delayed_list;/* stripes that have plugged requests */ struct list_head bitmap_list;/* stripes delaying awaiting bitmap update */ struct bio *retry_read_aligned;/* currently retrying aligned bios */ struct bio *retry_read_aligned_list;/* aligned bios retry list */ atomic_t preread_active_stripes;/* stripes with scheduled io */ atomic_t active_aligned_reads; +atomic_t pending_full_writes;/* full write backlog */ +int bypass_count;/* bypassed prereads */ +int bypass_threshold;/* preread nice */ +struct list_head *last_hold;/* detect hold_list promotions */ atomic_t reshape_stripes;/* stripes with pending writes for reshape */ /* unfortunately we need two cache names as we temporarily have
Quoted 9 lines · On Fri, 28 Mar 2008 16:45:28 +1100 NeilBrown <[email protected]> wrote:
drivers/md/raid5.c +9
-0+static ssize_t +raid5_store_preread_threshold (mddev_t *mddev,const char *page,size_t len) +{ +raid5_conf_t *conf =mddev_to_conf (mddev); +char *end; +int new; +if (len >=PAGE_SIZE ) +return -EINVAL ; +if (!conf)
drivers/md/raid5.c raid5_stripecache_size = __ATTR(stripe_c · line 4097
Quoted 10 lines
drivers/md/raid5.c +10
-0+return -ENODEV ; + + new =simple_strtoul (page, &end,10 ); +if (!*page || (*end && *end !='\n' )) +return -EINVAL ; +if (new > conf->max_nr_stripes || new <0 ) +return -EINVAL ; + conf->bypass_threshold = new; +return len; +}
checkpatch 0.16 (which I misfiled and have thus far failed to merge up) sayeth:
WARNING: consider using strict_strtoul in preference to simple_strtoul #258: FILE: drivers/md/raid5.c:4090:
+ new = simple_strtoul(page, &end, 10);
the reason being that code which uses simple_strtoul() can treat "42-what-a-todo" as "42", which seems a bit sloppy.
Your code won't have that failing, because it explicitly checks that the input ended in \0 or \n. But strict_strtoul() internally does that, so this open-coded test could be removed.
Quoted 29 lines · Andrew Morton
Quoted 9 lines · On Fri, 28 Mar 2008 16:45:28 +1100 NeilBrown <[email protected]> wrote:
(quoted diff) +9
-0+static ssize_t +raid5_store_preread_threshold(mddev_t *mddev, const char *page, size_t len) +{ + raid5_conf_t *conf = mddev_to_conf(mddev); + char *end; + int new; + if (len >= PAGE_SIZE) + return -EINVAL; + if (!conf)Review
Quoted 10 lines
(quoted diff) +10
-0+ return -ENODEV; + + new = simple_strtoul(page, &end, 10); + if (!*page || (*end && *end != '\n')) + return -EINVAL; + if (new > conf->max_nr_stripes || new < 0) + return -EINVAL; + conf->bypass_threshold = new; + return len; +}checkpatch 0.16 (which I misfiled and have thus far failed to merge up) sayeth:
WARNING: consider using
strict_strtoulin preference tosimple_strtoul#258: FILE:drivers/md/raid5.c:4090:
+ new =simple_strtoul(page, &end, 10);
Quoted 6 lines
the reason being that code which uses
simple_strtoul()can treat "42-what-a-todo" as "42", which seems a bit sloppy.Your code won't have that failing, because it explicitly checks that the input ended in \0 or \n. But
strict_strtoul()internally does that, so this open-coded test could be removed.
How about the following:
-------snip------>
Subject: md: raid5.c convert simple_strtoul to strict_strtoul
From: Dan Williams <[email protected]>
strict_strtoul handles the open-coded sanity checks in raid5_store_stripe_cache_size and raid5_store_preread_threshold
---
drivers/md/raid5.c | 14 +++++--------- 1 files changed, 5 insertions(+), 9 deletions(-)
drivers/md/raid5.c +5 -9
@@ -4034,15 +4034,13 @@ static ssize_t raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) {raid5_conf_t *conf =mddev_to_conf (mddev); -char *end; -int new; +unsigned long new;if (len >=PAGE_SIZE )return -EINVAL ;if (!conf)return -ENODEV ; - new =simple_strtoul (page, &end,10 ); -if (!*page || (*end && *end !='\n' ) ) +if (strict_strtoul (page,10 , &new))return -EINVAL ;if (new <=16 || new >32768 )return -EINVAL ;
@@ -4080,17 +4078,15 @@ static ssize_t raid5_store_preread_threshold(mddev_t *mddev, const char *page, size_t len) {raid5_conf_t *conf =mddev_to_conf (mddev); -char *end; -int new; +unsigned long new;if (len >=PAGE_SIZE )return -EINVAL ;if (!conf)return -ENODEV ; - new =simple_strtoul (page, &end,10 ); -if (!*page || (*end && *end !='\n' )) +if (strict_strtoul (page,10 , &new))return -EINVAL ; -if (new > conf->max_nr_stripes || new <0 ) +if (new > conf->max_nr_stripes || (int ) new <0 )return -EINVAL ; conf->bypass_threshold = new;return len;
NeilBrown
Acked-byQuoted 8 lines · Dan Williams
raid5_store_preread_threshold(mddev_t *mddev, const char *page,
size_t
len)
{drivers/md/raid5.c +1
-2raid5_conf_t *conf =mddev_to_conf (mddev); -char *end; -int new; +unsigned long new;if (len >=PAGE_SIZE )
drivers/md/raid5.c static ssize_t · line 4089
Quoted 10 lines
drivers/md/raid5.c +2
-3return -EINVAL ;if (!conf)return -ENODEV ; - new =simple_strtoul (page, &end,10 ); -if (!*page || (*end && *end !='\n' )) +if (strict_strtoul (page,10 , &new))return -EINVAL ; -if (new > conf->max_nr_stripes || new <0 ) +if (new > conf->max_nr_stripes || (int ) new <0 )
I had suggested that "new < 0" test when I saw that 'new' was an 'int'. A better suggestion would have been to make 'new' 'unsigned'. Now that you have done that, the "< 0" it pointless and should go.
Otherwise
Thanks,
NeilBrown
Dan Williams
Acked-by NeilBrownQuoted 20 lines · NeilBrown
Quoted 8 lines · Dan Williams
raid5_store_preread_threshold(mddev_t *mddev, const char *page,
size_t
len){ raid5_conf_t *conf = mddev_to_conf(mddev);(quoted diff) +1
-2- char *end; - int new; + unsigned long new; if (len >= PAGE_SIZE)Review
Quoted 10 lines
(quoted diff) +2
-3return -EINVAL; if (!conf) return -ENODEV; - new = simple_strtoul(page, &end, 10); - if (!*page || (*end && *end != '\n')) + if (strict_strtoul(page, 10, &new)) return -EINVAL; - if (new > conf->max_nr_stripes || new < 0) + if (new > conf->max_nr_stripes || (int) new < 0)
Quoted 4 lines
I had suggested that "new < 0" test when I saw that 'new' was an 'int'. A better suggestion would have been to make 'new' 'unsigned'. Now that you have done that, the "< 0" it pointless and should go.
Yeah, ->max_nr_stripes will always be less than (1 << 63).
Quoted 2 lines
Otherwise
Acked-by NeilBrown <[email protected]>
Thanks, new patch follows,
Dan
------snip------>
Subject: md: raid5.c convert simple_strtoul to strict_strtoul
From: Dan Williams <[email protected]>
strict_strtoul handles the open-coded sanity checks in raid5_store_stripe_cache_size and raid5_store_preread_threshold
---
drivers/md/raid5.c | 14 +++++--------- 1 files changed, 5 insertions(+), 9 deletions(-)
drivers/md/raid5.c +5 -9
@@ -4034,15 +4034,13 @@ static ssize_t raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) {raid5_conf_t *conf =mddev_to_conf (mddev); -char *end; -int new; +unsigned long new;if (len >=PAGE_SIZE )return -EINVAL ;if (!conf)return -ENODEV ; - new =simple_strtoul (page, &end,10 ); -if (!*page || (*end && *end !='\n' ) ) +if (strict_strtoul (page,10 , &new))return -EINVAL ;if (new <=16 || new >32768 )return -EINVAL ;
@@ -4080,17 +4078,15 @@ static ssize_t raid5_store_preread_threshold(mddev_t *mddev, const char *page, size_t len) {raid5_conf_t *conf =mddev_to_conf (mddev); -char *end; -int new; +unsigned long new;if (len >=PAGE_SIZE )return -EINVAL ;if (!conf)return -ENODEV ; - new =simple_strtoul (page, &end,10 ); -if (!*page || (*end && *end !='\n' )) +if (strict_strtoul (page,10 , &new))return -EINVAL ; -if (new > conf->max_nr_stripes || new <0 ) +if (new > conf->max_nr_stripes)return -EINVAL ; conf->bypass_threshold = new;return len;