Commit 616486ab authored by Michael Lyle's avatar Michael Lyle Committed by Jens Axboe

bcache: fix writeback target calc on large devices

Bcache needs to scale the dirty data in the cache over the multiple
backing disks in order to calculate writeback rates for each.
The previous code did this by multiplying the target number of dirty
sectors by the backing device size, and expected it to fit into a
uint64_t; this blows up on relatively small backing devices.

The new approach figures out the bdev's share in 16384ths of the overall
cached data.  This is chosen to cope well when bdevs drastically vary in
size and to ensure that bcache can cross the petabyte boundary for each
backing device.

This has been improved based on Tang Junhui's feedback to ensure that
every device gets a share of dirty data, no matter how small it is
compared to the total backing pool.

The existing mechanism is very limited; this is purely a bug fix to
remove limits on volume size.  However, there still needs to be change
to make this "fair" over many volumes where some are idle.
Reported-by: default avatarJack Douglas <jack@douglastechnology.co.uk>
Signed-off-by: default avatarMichael Lyle <mlyle@lyle.org>
Reviewed-by: default avatarTang Junhui <tang.junhui@zte.com.cn>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 5138ac67
...@@ -18,17 +18,39 @@ ...@@ -18,17 +18,39 @@
#include <trace/events/bcache.h> #include <trace/events/bcache.h>
/* Rate limiting */ /* Rate limiting */
static uint64_t __calc_target_rate(struct cached_dev *dc)
static void __update_writeback_rate(struct cached_dev *dc)
{ {
struct cache_set *c = dc->disk.c; struct cache_set *c = dc->disk.c;
/*
* This is the size of the cache, minus the amount used for
* flash-only devices
*/
uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size - uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size -
bcache_flash_devs_sectors_dirty(c); bcache_flash_devs_sectors_dirty(c);
/*
* Unfortunately there is no control of global dirty data. If the
* user states that they want 10% dirty data in the cache, and has,
* e.g., 5 backing volumes of equal size, we try and ensure each
* backing volume uses about 2% of the cache for dirty data.
*/
uint32_t bdev_share =
div64_u64(bdev_sectors(dc->bdev) << WRITEBACK_SHARE_SHIFT,
c->cached_dev_sectors);
uint64_t cache_dirty_target = uint64_t cache_dirty_target =
div_u64(cache_sectors * dc->writeback_percent, 100); div_u64(cache_sectors * dc->writeback_percent, 100);
int64_t target = div64_u64(cache_dirty_target * bdev_sectors(dc->bdev),
c->cached_dev_sectors);
/* Ensure each backing dev gets at least one dirty share */
if (bdev_share < 1)
bdev_share = 1;
return (cache_dirty_target * bdev_share) >> WRITEBACK_SHARE_SHIFT;
}
static void __update_writeback_rate(struct cached_dev *dc)
{
/* /*
* PI controller: * PI controller:
* Figures out the amount that should be written per second. * Figures out the amount that should be written per second.
...@@ -49,6 +71,7 @@ static void __update_writeback_rate(struct cached_dev *dc) ...@@ -49,6 +71,7 @@ static void __update_writeback_rate(struct cached_dev *dc)
* This acts as a slow, long-term average that is not subject to * This acts as a slow, long-term average that is not subject to
* variations in usage like the p term. * variations in usage like the p term.
*/ */
int64_t target = __calc_target_rate(dc);
int64_t dirty = bcache_dev_sectors_dirty(&dc->disk); int64_t dirty = bcache_dev_sectors_dirty(&dc->disk);
int64_t error = dirty - target; int64_t error = dirty - target;
int64_t proportional_scaled = int64_t proportional_scaled =
......
...@@ -8,6 +8,13 @@ ...@@ -8,6 +8,13 @@
#define MAX_WRITEBACKS_IN_PASS 5 #define MAX_WRITEBACKS_IN_PASS 5
#define MAX_WRITESIZE_IN_PASS 5000 /* *512b */ #define MAX_WRITESIZE_IN_PASS 5000 /* *512b */
/*
* 14 (16384ths) is chosen here as something that each backing device
* should be a reasonable fraction of the share, and not to blow up
* until individual backing devices are a petabyte.
*/
#define WRITEBACK_SHARE_SHIFT 14
static inline uint64_t bcache_dev_sectors_dirty(struct bcache_device *d) static inline uint64_t bcache_dev_sectors_dirty(struct bcache_device *d)
{ {
uint64_t i, ret = 0; uint64_t i, ret = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment