#ifndef WB_THROTTLE_H #define WB_THROTTLE_H #include #include #include #include #include #include "blk-stat.h" enum wbt_flags { WBT_TRACKED = 1, /* write, tracked for throttling */ WBT_READ = 2, /* read */ WBT_KSWAPD = 4, /* write, from kswapd */ WBT_NR_BITS = 3, /* number of bits */ }; enum { WBT_NUM_RWQ = 2, }; static inline void wbt_clear_state(struct blk_issue_stat *stat) { stat->time &= BLK_STAT_TIME_MASK; } static inline enum wbt_flags wbt_stat_to_mask(struct blk_issue_stat *stat) { return (stat->time & BLK_STAT_MASK) >> BLK_STAT_SHIFT; } static inline void wbt_track(struct blk_issue_stat *stat, enum wbt_flags wb_acct) { stat->time |= ((u64) wb_acct) << BLK_STAT_SHIFT; } static inline bool wbt_is_tracked(struct blk_issue_stat *stat) { return (stat->time >> BLK_STAT_SHIFT) & WBT_TRACKED; } static inline bool wbt_is_read(struct blk_issue_stat *stat) { return (stat->time >> BLK_STAT_SHIFT) & WBT_READ; } struct wb_stat_ops { void (*get)(void *, struct blk_rq_stat *); bool (*is_current)(struct blk_rq_stat *); void (*clear)(void *); }; struct rq_wait { wait_queue_head_t wait; atomic_t inflight; }; struct rq_wb { /* * Settings that govern how we throttle */ unsigned int wb_background; /* background writeback */ unsigned int wb_normal; /* normal writeback */ unsigned int wb_max; /* max throughput writeback */ int scale_step; bool scaled_max; /* * Number of consecutive periods where we don't have enough * information to make a firm scale up/down decision. */ unsigned int unknown_cnt; u64 win_nsec; /* default window size */ u64 cur_win_nsec; /* current window size */ struct timer_list window_timer; s64 sync_issue; void *sync_cookie; unsigned int wc; unsigned int queue_depth; unsigned long last_issue; /* last non-throttled issue */ unsigned long last_comp; /* last non-throttled comp */ unsigned long min_lat_nsec; struct request_queue *queue; struct rq_wait rq_wait[WBT_NUM_RWQ]; struct wb_stat_ops *stat_ops; void *ops_data; }; static inline unsigned int wbt_inflight(struct rq_wb *rwb) { unsigned int i, ret = 0; for (i = 0; i < WBT_NUM_RWQ; i++) ret += atomic_read(&rwb->rq_wait[i].inflight); return ret; } #ifdef CONFIG_BLK_WBT void __wbt_done(struct rq_wb *, enum wbt_flags); void wbt_done(struct rq_wb *, struct blk_issue_stat *); enum wbt_flags wbt_wait(struct rq_wb *, struct bio *, spinlock_t *); int wbt_init(struct request_queue *, struct wb_stat_ops *); void wbt_exit(struct request_queue *); void wbt_update_limits(struct rq_wb *); void wbt_requeue(struct rq_wb *, struct blk_issue_stat *); void wbt_issue(struct rq_wb *, struct blk_issue_stat *); void wbt_disable(struct rq_wb *); void wbt_set_queue_depth(struct rq_wb *, unsigned int); void wbt_set_write_cache(struct rq_wb *, bool); #else static inline void __wbt_done(struct rq_wb *rwb, enum wbt_flags flags) { } static inline void wbt_done(struct rq_wb *rwb, struct blk_issue_stat *stat) { } static inline enum wbt_flags wbt_wait(struct rq_wb *rwb, struct bio *bio, spinlock_t *lock) { return 0; } static inline int wbt_init(struct request_queue *q, struct wb_stat_ops *ops) { return -EINVAL; } static inline void wbt_exit(struct request_queue *q) { } static inline void wbt_update_limits(struct rq_wb *rwb) { } static inline void wbt_requeue(struct rq_wb *rwb, struct blk_issue_stat *stat) { } static inline void wbt_issue(struct rq_wb *rwb, struct blk_issue_stat *stat) { } static inline void wbt_disable(struct rq_wb *rwb) { } static inline void wbt_set_queue_depth(struct rq_wb *rwb, unsigned int depth) { } static inline void wbt_set_write_cache(struct rq_wb *rwb, bool wc) { } #endif /* CONFIG_BLK_WBT */ #endif