huawei-mrd-kernel/lib/wbt.c

756 lines
17 KiB
C

/*
* buffered writeback throttling. losely based on CoDel. We can't drop
* packets for IO scheduling, so the logic is something like this:
*
* - Monitor latencies in a defined window of time.
* - If the minimum latency in the above window exceeds some target, increment
* scaling step and scale down queue depth by a factor of 2x. The monitoring
* window is then shrunk to 100 / sqrt(scaling step + 1).
* - For any window where we don't have solid data on what the latencies
* look like, retain status quo.
* - If latencies look good, decrement scaling step.
*
* Copyright (C) 2016 Jens Axboe
*
* Things that (may) need changing:
*
* - Different scaling of background/normal/high priority writeback.
* We may have to violate guarantees for max.
* - We can have mismatches between the stat window and our window.
*
*/
#include <linux/kernel.h>
#include <linux/blk_types.h>
#include <linux/slab.h>
#include <linux/backing-dev.h>
#include <linux/wbt.h>
#include <linux/blkdev.h>
#define CREATE_TRACE_POINTS
#include <trace/events/wbt.h>
enum {
/*
* Might need to be higher
*/
RWB_MAX_DEPTH = 64,
/*
* 100msec window
*/
RWB_WINDOW_NSEC = 100 * 1000 * 1000ULL,
/*
* Disregard stats, if we don't meet these minimums
*/
RWB_MIN_WRITE_SAMPLES = 3,
RWB_MIN_READ_SAMPLES = 1,
/*
* If we have this number of consecutive windows with not enough
* information to scale up or down, scale up.
*/
RWB_UNKNOWN_BUMP = 5,
};
static inline bool rwb_enabled(struct rq_wb *rwb)
{
return rwb && rwb->wb_normal != 0;
}
/*
* Increment 'v', if 'v' is below 'below'. Returns true if we succeeded,
* false if 'v' + 1 would be bigger than 'below'.
*/
static bool atomic_inc_below(atomic_t *v, int below)
{
/*lint -save -e438 -e529 -e571*/
int cur = atomic_read(v);
for (;;) {
int old;
if (cur >= below)
return false;
old = atomic_cmpxchg(v, cur, cur + 1);
if (old == cur)
break;
cur = old;
}
/*lint -restore*/
return true;
}
static void wb_timestamp(struct rq_wb *rwb, unsigned long *var)
{
if (rwb_enabled(rwb)) {
const unsigned long cur = jiffies;
if (cur != *var)
*var = cur;
}
}
void __wbt_done(struct rq_wb *rwb)
{
int inflight, limit;
inflight = atomic_dec_return(&rwb->inflight);
/*
* wbt got disabled with IO in flight. Wake up any potential
* waiters, we don't have to do more than that.
*/
/*lint -save -e730*/
if (unlikely(!rwb_enabled(rwb))) {
wake_up_all(&rwb->wait);
return;
}
/*lint -restore*/
/*
* If the device does write back caching, drop further down
* before we wake people up.
*/
/*lint -save -e438 -e529 -e713*/
if (rwb->wc && !atomic_read(&rwb->bdi->wb.dirty_sleeping))
limit = 0;
else
limit = rwb->wb_normal;
/*lint -restore*/
/*
* Don't wake anyone up if we are above the normal limit.
*/
if (inflight && inflight >= limit)
return;
if (waitqueue_active(&rwb->wait)) {
int diff = limit - inflight;
/*lint -save -e574 -e737*/
if (!inflight || diff >= rwb->wb_background / 2)
/*lint -restore*/
wake_up_nr(&rwb->wait, 1);
}
}
/*
* Called on completion of a request. Note that it's also called when
* a request is merged, when the request gets freed.
*/
void wbt_done(struct rq_wb *rwb, struct wb_issue_stat *stat, bool fg)
{
if (!rwb)
return;
if (!wbt_tracked(stat)) {
if (rwb->sync_cookie == stat) {
rwb->sync_issue = 0;
rwb->sync_cookie = NULL;
}
#ifdef CONFIG_BLK_DEV_THROTTLING
if (fg)
#endif
wb_timestamp(rwb, &rwb->last_comp);
} else {
/*lint -save -e727 -e730*/
WARN_ON_ONCE(stat == rwb->sync_cookie);
/*lint -restore*/
__wbt_done(rwb);
wbt_clear_tracked(stat);
}
}
static void calc_wb_limits(struct rq_wb *rwb)
{
unsigned int depth;
if (!rwb->min_lat_nsec) {
rwb->wb_max = rwb->wb_normal = rwb->wb_background = 0;
return;
}
/*
* For QD=1 devices, this is a special case. It's important for those
* to have one request ready when one completes, so force a depth of
* 2 for those devices. On the backend, it'll be a depth of 1 anyway,
* since the device can't have more than that in flight. If we're
* scaling down, then keep a setting of 1/1/1.
*/
if (rwb->queue_depth == 1) {
if (rwb->scale_step)
rwb->wb_max = rwb->wb_normal = 1;
else
rwb->wb_max = rwb->wb_normal = 2;
rwb->wb_background = 1;
} else {
depth = min_t(unsigned int, RWB_MAX_DEPTH, rwb->queue_depth);
/*
* Set our max/normal/bg queue depths based on how far
* we have scaled down (->scale_step).
*/
/*lint -save -e713*/
rwb->wb_max = 1 + ((depth - 1) >> min(31U, rwb->scale_step));
/*lint -restore*/
rwb->wb_normal = (rwb->wb_max + 1) / 2;
rwb->wb_background = (rwb->wb_max + 3) / 4;
}
}
static bool inline stat_sample_valid(struct blk_rq_stat *stat)
{
/*
* We need at least one read sample, and a minimum of
* RWB_MIN_WRITE_SAMPLES. We require some write samples to know
* that it's writes impacting us, and not just some sole read on
* a device that is in a lower power state.
*/
#ifdef CONFIG_BLK_DEV_THROTTLING
return stat[2].nr_samples >= RWB_MIN_READ_SAMPLES &&
(stat[1].nr_samples - stat[3].nr_samples) >= RWB_MIN_WRITE_SAMPLES;
#else
return stat[0].nr_samples >= RWB_MIN_READ_SAMPLES &&
stat[1].nr_samples >= RWB_MIN_WRITE_SAMPLES;
#endif
}
static u64 rwb_sync_issue_lat(struct rq_wb *rwb)
{
/*lint -save -e438 -e529 -e732*/
u64 now, issue = ACCESS_ONCE(rwb->sync_issue);
/*lint -restore*/
if (!issue || !rwb->sync_cookie)
return 0;
/*lint -save -e732*/
now = ktime_to_ns(ktime_get());
/*lint -restore*/
return now - issue;
}
enum {
LAT_OK,
LAT_UNKNOWN,
LAT_EXCEEDED,
};
static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
{
u64 thislat;
u32 io_type;
#ifdef CONFIG_BLK_DEV_THROTTLING
io_type = 2;
#else
io_type = 0;
#endif
/*
* If our stored sync issue exceeds the window size, or it
* exceeds our min target AND we haven't logged any entries,
* flag the latency as exceeded. wbt works off completion latencies,
* but for a flooded device, a single sync IO can take a long time
* to complete after being issued. If this time exceeds our
* monitoring window AND we didn't see any other completions in that
* window, then count that sync IO as a violation of the latency.
*/
thislat = rwb_sync_issue_lat(rwb);
if (thislat > rwb->cur_win_nsec ||
(thislat > rwb->min_lat_nsec && !stat[io_type].nr_samples)) {
trace_wbt_lat(rwb->bdi, thislat);
return LAT_EXCEEDED;
}
if (!stat_sample_valid(stat))
return LAT_UNKNOWN;
/*
* If the 'min' latency exceeds our target, step down.
*/
if (stat[io_type].min > rwb->min_lat_nsec) {
trace_wbt_lat(rwb->bdi, stat[io_type].min);
trace_wbt_stat(rwb->bdi, stat);
return LAT_EXCEEDED;
}
if (rwb->scale_step) {
trace_wbt_stat(rwb->bdi, stat);
}
return LAT_OK;
}
static int latency_exceeded(struct rq_wb *rwb)
{
struct blk_rq_stat stat[4];
rwb->stat_ops->get(rwb->ops_data, stat);
return __latency_exceeded(rwb, stat);
}
static void rwb_trace_step(struct rq_wb *rwb, const char *msg)
{
trace_wbt_step(rwb->bdi, msg, rwb->scale_step, rwb->cur_win_nsec,
rwb->wb_background, rwb->wb_normal, rwb->wb_max);
}
static void scale_up(struct rq_wb *rwb)
{
/*
* If we're at 0, we can't go lower.
*/
if (!rwb->scale_step)
return;
rwb->scale_step--;
rwb->unknown_cnt = 0;
rwb->ok_cnt = 0;
rwb->stat_ops->clear(rwb->ops_data);
calc_wb_limits(rwb);
if (waitqueue_active(&rwb->wait))
wake_up_all(&rwb->wait);
rwb_trace_step(rwb, "step up");
}
static void scale_down(struct rq_wb *rwb)
{
/*
* Stop scaling down when we've hit the limit. This also prevents
* ->scale_step from going to crazy values, if the device can't
* keep up.
*/
if (rwb->wb_max == 1)
return;
rwb->scale_step++;
rwb->unknown_cnt = 0;
rwb->ok_cnt = 0;
rwb->stat_ops->clear(rwb->ops_data);
calc_wb_limits(rwb);
rwb_trace_step(rwb, "step down");
}
static void rwb_arm_timer(struct rq_wb *rwb)
{
unsigned long expires;
/*
* We should speed this up, using some variant of a fast integer
* inverse square root calculation. Since we only do this for
* every window expiration, it's not a huge deal, though.
*/
/*lint -save -e647 -e712 -e747*/
rwb->cur_win_nsec = div_u64(rwb->win_nsec << 4,
int_sqrt((rwb->scale_step + 3) << 8));
/*lint -restore*/
expires = jiffies + nsecs_to_jiffies(rwb->cur_win_nsec);
mod_timer(&rwb->window_timer, expires);
}
static void wb_timer_fn(unsigned long data)
{
struct rq_wb *rwb = (struct rq_wb *) data;
int status;
/*
* If we exceeded the latency target, step down. If we did not,
* step one level up. If we don't know enough to say either exceeded
* or ok, then don't do anything.
*/
status = latency_exceeded(rwb);
switch (status) {
case LAT_EXCEEDED:
scale_down(rwb);
break;
case LAT_OK:
if (++rwb->ok_cnt >= rwb->ok_cnt_set)
scale_up(rwb);
break;
case LAT_UNKNOWN:
/*
* We had no read samples, start bumping up the write
* depth slowly
*/
if (++rwb->unknown_cnt >= RWB_UNKNOWN_BUMP)
scale_up(rwb);
break;
default:
break;
}
/*
* Re-arm timer, if we have IO in flight
*/
/*lint -save -e438 -e529*/
if (rwb->scale_step || atomic_read(&rwb->inflight))
/*lint -restore*/
rwb_arm_timer(rwb);
}
void wbt_update_limits(struct rq_wb *rwb)
{
rwb->scale_step = 0;
calc_wb_limits(rwb);
if (waitqueue_active(&rwb->wait))
wake_up_all(&rwb->wait);
}
static bool close_io(struct rq_wb *rwb)
{
const unsigned long now = jiffies;
/*lint -save -e550*/
return time_before(now, rwb->last_issue + HZ / 10) ||
time_before(now, rwb->last_comp + HZ / 10);
/*lint -restore*/
}
#define REQ_HIPRIO (REQ_SYNC | REQ_META | REQ_PRIO)
static inline unsigned int get_limit(struct rq_wb *rwb, unsigned long rw)
{
unsigned int limit;
/*
* At this point we know it's a buffered write. If REQ_SYNC is
* set, then it's WB_SYNC_ALL writeback, and we'll use the max
* limit for that. If the write is marked as a background write,
* then use the idle limit, or go to normal if we haven't had
* competing IO for a bit.
*/
/*lint -save -e438 -e529*/
if ((rw & REQ_HIPRIO) || atomic_read(&rwb->bdi->wb.dirty_sleeping))
/*lint -restore*/
limit = rwb->wb_max;
else if ((rw & REQ_BG) || close_io(rwb)) {
/*
* If less than 100ms since we completed unrelated IO,
* limit us to half the depth for background writeback.
*/
limit = rwb->wb_background;
} else
limit = rwb->wb_normal;
return limit;
}
static inline bool may_queue(struct rq_wb *rwb, unsigned long rw)
{
/*
* inc it here even if disabled, since we'll dec it at completion.
* this only happens if the task was sleeping in __wbt_wait(),
* and someone turned it off at the same time.
*/
if (!rwb_enabled(rwb) || wbt_mode(rwb) != WBT_BLK) {
atomic_inc(&rwb->inflight);
return true;
}
/*lint -save -e713*/
return atomic_inc_below(&rwb->inflight, get_limit(rwb, rw));
/*lint -restore*/
}
/*
* Block if we will exceed our limit, or if we are currently waiting for
* the timer to kick off queuing again.
*/
static void __wbt_wait(struct rq_wb *rwb, unsigned long rw, spinlock_t *lock)
{
/*lint -save -e446*/
DEFINE_WAIT(wait);
/*lint -restore*/
if (may_queue(rwb, rw))
return;
do {
prepare_to_wait_exclusive(&rwb->wait, &wait,
TASK_UNINTERRUPTIBLE);
if (may_queue(rwb, rw))
break;
if (lock)
spin_unlock_irq(lock);
io_schedule();
if (lock)
spin_lock_irq(lock);
} while (1);
finish_wait(&rwb->wait, &wait);
}
/*lint -save -e715*/
static inline bool wbt_should_throttle(struct rq_wb *rwb, unsigned int rw)
{
const int op = rw >> BIO_OP_SHIFT;
/*
* If not a WRITE (or a discard), do nothing
*/
if ((op == REQ_OP_DISCARD) || (op != REQ_OP_WRITE)) {
return false;
}
if ((rw & REQ_META) || (rw & REQ_SYNC)){
return false;
}
/*
* Don't throttle WRITE_ODIRECT
*/
if ((rw & (REQ_SYNC | REQ_NOIDLE)) == REQ_SYNC)
return false;
return true;
}
/*lint -restore*/
/*
* It's a simply and rough way to kick bio, bio which
* has been tracked by wbt(on fs) set NOMERGE after bio_alloc,
* so we kick the bio with NOMERGE once it's full of pages.
*/
bool wbt_need_kick_bio(struct bio *bio)
{
return (bio->bi_vcnt == bio->bi_max_vecs)
&& (bio->bi_opf & REQ_NOMERGE);
}
static void wbt_add_inflight(struct rq_wb *rwb)
{
atomic_inc(&rwb->inflight);
}
/*
* Returns true if the IO request should be accounted, false if not.
* May sleep, if we have exceeded the writeback limits. Caller can pass
* in an irq held spinlock, if it holds one when calling this function.
* If we do sleep, we'll release and re-grab it.
*/
bool wbt_wait(struct rq_wb *rwb, unsigned int rw, spinlock_t *lock)
{
if (!rwb_enabled(rwb))
return false;
if (!wbt_should_throttle(rwb, rw)) {
wb_timestamp(rwb, &rwb->last_issue);
return false;
}
/*lint -save -e747*/
if (wbt_mode(rwb) == WBT_FS)
wbt_add_inflight(rwb);
else
__wbt_wait(rwb, rw, lock);
/*lint -restore*/
if (!timer_pending(&rwb->window_timer))
rwb_arm_timer(rwb);
return true;
}
/* min wbt io size = 128k(32 pages) */
#define wbt_min_sectors 32
int wbt_max_bio_blocks(struct block_device *bdev, int rw,
int max, bool *nomerge)
{
int wbt_max;
struct request_queue *q;
struct rq_wb *rwb;
wbt_max = max;
*nomerge = false;
if (unlikely(!bdev) || unlikely(!bdev->bd_disk))
goto out;
q = bdev_get_queue(bdev);
if (unlikely(!q))
goto out;
rwb = q->rq_wb;
if (!rwb_enabled(rwb) || (wbt_mode(rwb) != WBT_FS)
|| !wbt_should_throttle(rwb, rw))
goto out;
wbt_max = max;
if (rwb->scale_step) {
wbt_max = wbt_min_sectors;
*nomerge = true;
}
out:
return wbt_max;
}
static unsigned int wbt_get_wbc_limit(struct rq_wb *rwb,
struct writeback_control *wbc)
{
unsigned long rw = 0;
if (wbc->for_kupdate || wbc->for_background)
rw |= REQ_BG;
if (wbc->sync_mode == WB_SYNC_ALL || wbc->for_sync == 1)
rw |= REQ_SYNC;
return get_limit(rwb, rw);
}
bool wbt_fs_get_quota(struct request_queue *q, struct writeback_control *wbc)
{
struct rq_wb *rwb = q->rq_wb;
if (!rwb_enabled(rwb) || wbt_mode(rwb) != WBT_FS
|| wbc->sync_mode == WB_SYNC_ALL
|| wbc->for_sync == 1)
return true;
/*lint -save -e574*/
if (atomic_read(&rwb->inflight) < wbt_get_wbc_limit(rwb, wbc))
return true;
/*lint -restore*/
return false;
}
void wbt_fs_wait(struct request_queue *q, struct writeback_control *wbc)
{
unsigned int limit;
DEFINE_WAIT(wait);
struct rq_wb *rwb = q->rq_wb;
limit = wbt_get_wbc_limit(rwb, wbc);
/*
* inc it here even if disabled, since we'll dec it at completion.
* this only happens if the task was sleeping in __wbt_wait(),
* and someone turned it off at the same time.
*/
/*lint -save -e574*/
if (!rwb_enabled(rwb)
|| atomic_read(&rwb->inflight) < limit)
return;
/*lint -restore*/
do {
prepare_to_wait_exclusive(&rwb->wait, &wait,
TASK_UNINTERRUPTIBLE);
if (wbt_mode(rwb) != WBT_FS)
break;
/*lint -save -e574*/
if (!rwb_enabled(rwb)
|| atomic_read(&rwb->inflight) < limit)
break;
/*lint -restore*/
io_schedule();
} while (1);
finish_wait(&rwb->wait, &wait);
}
void wbt_issue(struct rq_wb *rwb, struct wb_issue_stat *stat, bool fg)
{
if (!rwb_enabled(rwb))
return;
wbt_issue_stat_set_time(stat);
/*
* Track sync issue, in case it takes a long time to complete. Allows
* us to react quicker, if a sync IO takes a long time to complete.
* Note that this is just a hint. 'stat' can go away when the
* request completes, so it's important we never dereference it. We
* only use the address to compare with, which is why we store the
* sync_issue time locally.
*/
/*lint -save -e713*/
if (!wbt_tracked(stat) && !rwb->sync_issue) {
#ifdef CONFIG_BLK_DEV_THROTTLING
if (fg)
#endif
{
rwb->sync_cookie = stat;
rwb->sync_issue = wbt_issue_stat_get_time(stat);
}
}
/*lint -restore*/
}
void wbt_requeue(struct rq_wb *rwb, struct wb_issue_stat *stat)
{
if (!rwb_enabled(rwb))
return;
if (stat == rwb->sync_cookie) {
rwb->sync_issue = 0;
rwb->sync_cookie = NULL;
}
}
void wbt_set_queue_depth(struct rq_wb *rwb, unsigned int depth)
{
if (rwb) {
rwb->queue_depth = depth;
wbt_update_limits(rwb);
}
}
void wbt_set_write_cache(struct rq_wb *rwb, bool write_cache_on)
{
if (rwb)
rwb->wc = write_cache_on;
}
void wbt_disable(struct rq_wb *rwb)
{
del_timer_sync(&rwb->window_timer);
rwb->win_nsec = rwb->min_lat_nsec = 0;
wbt_update_limits(rwb);
}
EXPORT_SYMBOL_GPL(wbt_disable);
struct rq_wb *wbt_init(struct backing_dev_info *bdi, struct wb_stat_ops *ops,
void *ops_data)
{
struct rq_wb *rwb;
rwb = kzalloc(sizeof(*rwb), GFP_KERNEL);
/*lint -save -e747 -e1058*/
if (!rwb)
return ERR_PTR(-ENOMEM);
atomic_set(&rwb->inflight, 0);
init_waitqueue_head(&rwb->wait);
setup_timer(&rwb->window_timer, wb_timer_fn, (unsigned long) rwb);
rwb->wc = 1;
rwb->queue_depth = RWB_MAX_DEPTH;
rwb->last_comp = rwb->last_issue = jiffies;
rwb->bdi = bdi;
rwb->win_nsec = RWB_WINDOW_NSEC;
rwb->stat_ops = ops,
rwb->ops_data = ops_data;
wbt_update_limits(rwb);
return rwb;
/*lint -restore*/
}
void wbt_exit(struct rq_wb *rwb)
{
if (rwb) {
del_timer_sync(&rwb->window_timer);
kfree(rwb);
}
}