Skip to content
Permalink
Browse files
bcache: consider the fragmentation when update the writeback rate
Current way to calculate the writeback rate only considered the
dirty sectors, this usually works fine when the fragmentation
is not high, but it will give us unreasonable small rate when
we are under a situation that very few dirty sectors consumed
a lot dirty buckets. In some case, the dirty bucekts can reached
to CUTOFF_WRITEBACK_SYNC while the dirty data(sectors) noteven
reached the writeback_percent, the writeback rate will still
be the minimum value (4k), thus it will cause all the writes to be
stucked in a non-writeback mode because of the slow writeback.

We accelerate the rate in 3 stages with different aggressiveness,
the first stage starts when dirty buckets percent reach above
BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW (50), the second is
BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID (57), the third is
BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH (64). By default
the first stage tries to writeback the amount of dirty data
in one bucket (on average) in (1 / (dirty_buckets_percent - 50)) second,
the second stage tries to writeback the amount of dirty data in one bucket
in (1 / (dirty_buckets_percent - 57)) * 200 millisecond, the third
stage tries to writeback the amount of dirty data in one bucket in
(1 / (dirty_buckets_percent - 64)) * 20 millisecond.

Signed-off-by: dongdong tao <dongdong.tao@canonical.com>
  • Loading branch information
taodd authored and intel-lab-lkp committed Jan 5, 2021
1 parent e71ba94 commit 7777fef68d1401235db42dd0d59c5c3dba3d42d3
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 0 deletions.
@@ -385,6 +385,9 @@ struct cached_dev {
unsigned int writeback_rate_update_seconds;
unsigned int writeback_rate_i_term_inverse;
unsigned int writeback_rate_p_term_inverse;
unsigned int writeback_rate_fp_term_low;
unsigned int writeback_rate_fp_term_mid;
unsigned int writeback_rate_fp_term_high;
unsigned int writeback_rate_minimum;

enum stop_on_failure stop_when_cache_set_failed;
@@ -121,6 +121,9 @@ rw_attribute(writeback_rate);
rw_attribute(writeback_rate_update_seconds);
rw_attribute(writeback_rate_i_term_inverse);
rw_attribute(writeback_rate_p_term_inverse);
rw_attribute(writeback_rate_fp_term_low);
rw_attribute(writeback_rate_fp_term_mid);
rw_attribute(writeback_rate_fp_term_high);
rw_attribute(writeback_rate_minimum);
read_attribute(writeback_rate_debug);

@@ -205,6 +208,9 @@ SHOW(__bch_cached_dev)
var_print(writeback_rate_update_seconds);
var_print(writeback_rate_i_term_inverse);
var_print(writeback_rate_p_term_inverse);
var_print(writeback_rate_fp_term_low);
var_print(writeback_rate_fp_term_mid);
var_print(writeback_rate_fp_term_high);
var_print(writeback_rate_minimum);

if (attr == &sysfs_writeback_rate_debug) {
@@ -331,6 +337,15 @@ STORE(__cached_dev)
sysfs_strtoul_clamp(writeback_rate_p_term_inverse,
dc->writeback_rate_p_term_inverse,
1, UINT_MAX);
sysfs_strtoul_clamp(writeback_rate_fp_term_low,
dc->writeback_rate_fp_term_low,
1, UINT_MAX);
sysfs_strtoul_clamp(writeback_rate_fp_term_mid,
dc->writeback_rate_fp_term_mid,
1, UINT_MAX);
sysfs_strtoul_clamp(writeback_rate_fp_term_high,
dc->writeback_rate_fp_term_high,
1, UINT_MAX);
sysfs_strtoul_clamp(writeback_rate_minimum,
dc->writeback_rate_minimum,
1, UINT_MAX);
@@ -502,6 +517,9 @@ static struct attribute *bch_cached_dev_files[] = {
&sysfs_writeback_rate_update_seconds,
&sysfs_writeback_rate_i_term_inverse,
&sysfs_writeback_rate_p_term_inverse,
&sysfs_writeback_rate_fp_term_low,
&sysfs_writeback_rate_fp_term_mid,
&sysfs_writeback_rate_fp_term_high,
&sysfs_writeback_rate_minimum,
&sysfs_writeback_rate_debug,
&sysfs_io_errors,
@@ -88,6 +88,42 @@ static void __update_writeback_rate(struct cached_dev *dc)
int64_t integral_scaled;
uint32_t new_rate;

/*
* We need to consider the number of dirty buckets as well
* when calculating the proportional_scaled, Otherwise we might
* have an unreasonable small writeback rate at a highly fragmented situation
* when very few dirty sectors consumed a lot dirty buckets, the
* worst case is when dirty_data reached writeback_percent and
* dirty buckets reached to cutoff_writeback_sync, but the rate
* still will be at the minimum value, which will cause the write
* stuck at a non-writeback mode.
*/
struct cache_set *c = dc->disk.c;

int64_t dirty_buckets = c->nbuckets - c->avail_nbuckets;

if (c->gc_stats.in_use > BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW && dirty > 0) {
int64_t fragment = (dirty_buckets * c->cache->sb.bucket_size) / dirty;
int64_t fp_term;
int64_t fps;

if (c->gc_stats.in_use <= BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID) {
fp_term = dc->writeback_rate_fp_term_low *
(c->gc_stats.in_use - BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW);
} else if (c->gc_stats.in_use <= BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH) {
fp_term = dc->writeback_rate_fp_term_mid *
(c->gc_stats.in_use - BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID);
} else {
fp_term = dc->writeback_rate_fp_term_high *
(c->gc_stats.in_use - BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH);
}
fps = (dirty / dirty_buckets) * fp_term;
if (fragment > 3 && fps > proportional_scaled) {
//Only overrite the p when fragment > 3
proportional_scaled = fps;
}
}

if ((error < 0 && dc->writeback_rate_integral > 0) ||
(error > 0 && time_before64(local_clock(),
dc->writeback_rate.next + NSEC_PER_MSEC))) {
@@ -984,6 +1020,9 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc)

dc->writeback_rate_update_seconds = WRITEBACK_RATE_UPDATE_SECS_DEFAULT;
dc->writeback_rate_p_term_inverse = 40;
dc->writeback_rate_fp_term_low = 1;
dc->writeback_rate_fp_term_mid = 5;
dc->writeback_rate_fp_term_high = 50;
dc->writeback_rate_i_term_inverse = 10000;

WARN_ON(test_and_clear_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags));
@@ -16,6 +16,10 @@

#define BCH_AUTO_GC_DIRTY_THRESHOLD 50

#define BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW 50
#define BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID 57
#define BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH 64

#define BCH_DIRTY_INIT_THRD_MAX 64
/*
* 14 (16384ths) is chosen here as something that each backing device

0 comments on commit 7777fef

Please sign in to comment.