X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=block%2Fblk-wbt.c;h=520ac06955748f0125f6def5391d5e12bba14b20;hb=462e466251157a14d12e80b756e9639d888cae88;hp=ae8de9780085ae7b8e99237ed16fc9cd02b233a5;hpb=9ad95bdacaa3296460fc23e31feaead1d4ffc8b9;p=mirror_ubuntu-bionic-kernel.git diff --git a/block/blk-wbt.c b/block/blk-wbt.c index ae8de9780085..520ac0695574 100644 --- a/block/blk-wbt.c +++ b/block/blk-wbt.c @@ -101,9 +101,13 @@ static bool wb_recent_wait(struct rq_wb *rwb) return time_before(jiffies, wb->dirty_sleep + HZ); } -static inline struct rq_wait *get_rq_wait(struct rq_wb *rwb, bool is_kswapd) +static inline struct rq_wait *get_rq_wait(struct rq_wb *rwb, + enum wbt_flags wb_acct) { - return &rwb->rq_wait[is_kswapd]; + if (wb_acct & WBT_KSWAPD) + return &rwb->rq_wait[WBT_RWQ_KSWAPD]; + + return &rwb->rq_wait[WBT_RWQ_BG]; } static void rwb_wake_all(struct rq_wb *rwb) @@ -113,20 +117,16 @@ static void rwb_wake_all(struct rq_wb *rwb) for (i = 0; i < WBT_NUM_RWQ; i++) { struct rq_wait *rqw = &rwb->rq_wait[i]; - if (waitqueue_active(&rqw->wait)) + if (wq_has_sleeper(&rqw->wait)) wake_up_all(&rqw->wait); } } -void __wbt_done(struct rq_wb *rwb, enum wbt_flags wb_acct) +static void wbt_rqw_done(struct rq_wb *rwb, struct rq_wait *rqw, + enum wbt_flags wb_acct) { - struct rq_wait *rqw; int inflight, limit; - if (!(wb_acct & WBT_TRACKED)) - return; - - rqw = get_rq_wait(rwb, wb_acct & WBT_KSWAPD); inflight = atomic_dec_return(&rqw->inflight); /* @@ -153,7 +153,7 @@ void __wbt_done(struct rq_wb *rwb, enum wbt_flags wb_acct) if (inflight && inflight >= limit) return; - if (waitqueue_active(&rqw->wait)) { + if (wq_has_sleeper(&rqw->wait)) { int diff = limit - inflight; if (!inflight || diff >= rwb->wb_background / 2) @@ -161,6 +161,17 @@ void __wbt_done(struct rq_wb *rwb, enum wbt_flags wb_acct) } } +void __wbt_done(struct rq_wb *rwb, enum wbt_flags wb_acct) +{ + struct rq_wait *rqw; + + if (!(wb_acct & WBT_TRACKED)) + return; + + rqw = get_rq_wait(rwb, wb_acct); + wbt_rqw_done(rwb, rqw, wb_acct); +} + /* * Called on completion of a request. Note that it's also called when * a request is merged, when the request gets freed. @@ -479,6 +490,13 @@ static inline unsigned int get_limit(struct rq_wb *rwb, unsigned long rw) { unsigned int limit; + /* + * If we got disabled, just return UINT_MAX. This ensures that + * we'll properly inc a new IO, and dec+wakeup at the end. + */ + if (!rwb_enabled(rwb)) + return UINT_MAX; + /* * At this point we know it's a buffered write. If this is * kswapd trying to free memory, or REQ_SYNC is set, then @@ -501,50 +519,78 @@ static inline unsigned int get_limit(struct rq_wb *rwb, unsigned long rw) return limit; } -static inline bool may_queue(struct rq_wb *rwb, struct rq_wait *rqw, - wait_queue_entry_t *wait, unsigned long rw) +struct wbt_wait_data { + struct wait_queue_entry wq; + struct task_struct *task; + struct rq_wb *rwb; + struct rq_wait *rqw; + unsigned long rw; + bool got_token; +}; + +static int wbt_wake_function(struct wait_queue_entry *curr, unsigned int mode, + int wake_flags, void *key) { - /* - * 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)) { - atomic_inc(&rqw->inflight); - return true; - } + struct wbt_wait_data *data = container_of(curr, struct wbt_wait_data, + wq); /* - * If the waitqueue is already active and we are not the next - * in line to be woken up, wait for our turn. + * If we fail to get a budget, return -1 to interrupt the wake up + * loop in __wake_up_common. */ - if (waitqueue_active(&rqw->wait) && - rqw->wait.head.next != &wait->entry) - return false; + if (!atomic_inc_below(&data->rqw->inflight, get_limit(data->rwb, data->rw))) + return -1; - return atomic_inc_below(&rqw->inflight, get_limit(rwb, rw)); + data->got_token = true; + list_del_init(&curr->entry); + wake_up_process(data->task); + return 1; } /* * 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) +static void __wbt_wait(struct rq_wb *rwb, enum wbt_flags wb_acct, + unsigned long rw, spinlock_t *lock) __releases(lock) __acquires(lock) { - struct rq_wait *rqw = get_rq_wait(rwb, current_is_kswapd()); - DEFINE_WAIT(wait); - - if (may_queue(rwb, rqw, &wait, rw)) + struct rq_wait *rqw = get_rq_wait(rwb, wb_acct); + struct wbt_wait_data data = { + .wq = { + .func = wbt_wake_function, + .entry = LIST_HEAD_INIT(data.wq.entry), + }, + .task = current, + .rwb = rwb, + .rqw = rqw, + .rw = rw, + }; + bool has_sleeper; + + has_sleeper = wq_has_sleeper(&rqw->wait); + if (!has_sleeper && atomic_inc_below(&rqw->inflight, get_limit(rwb, rw))) return; + prepare_to_wait_exclusive(&rqw->wait, &data.wq, TASK_UNINTERRUPTIBLE); do { - prepare_to_wait_exclusive(&rqw->wait, &wait, - TASK_UNINTERRUPTIBLE); + if (data.got_token) + break; - if (may_queue(rwb, rqw, &wait, rw)) + if (!has_sleeper && + atomic_inc_below(&rqw->inflight, get_limit(rwb, rw))) { + finish_wait(&rqw->wait, &data.wq); + + /* + * We raced with wbt_wake_function() getting a token, + * which means we now have two. Put our local token + * and wake anyone else potentially waiting for one. + */ + if (data.got_token) + wbt_rqw_done(rwb, rqw, wb_acct); break; + } if (lock) { spin_unlock_irq(lock); @@ -552,9 +598,11 @@ static void __wbt_wait(struct rq_wb *rwb, unsigned long rw, spinlock_t *lock) spin_lock_irq(lock); } else io_schedule(); + + has_sleeper = false; } while (1); - finish_wait(&rqw->wait, &wait); + finish_wait(&rqw->wait, &data.wq); } static inline bool wbt_should_throttle(struct rq_wb *rwb, struct bio *bio) @@ -584,7 +632,7 @@ static inline bool wbt_should_throttle(struct rq_wb *rwb, struct bio *bio) */ enum wbt_flags wbt_wait(struct rq_wb *rwb, struct bio *bio, spinlock_t *lock) { - unsigned int ret = 0; + enum wbt_flags ret = 0; if (!rwb_enabled(rwb)) return 0; @@ -598,14 +646,14 @@ enum wbt_flags wbt_wait(struct rq_wb *rwb, struct bio *bio, spinlock_t *lock) return ret; } - __wbt_wait(rwb, bio->bi_opf, lock); + if (current_is_kswapd()) + ret |= WBT_KSWAPD; + + __wbt_wait(rwb, ret, bio->bi_opf, lock); if (!blk_stat_is_active(rwb->cb)) rwb_arm_timer(rwb); - if (current_is_kswapd()) - ret |= WBT_KSWAPD; - return ret | WBT_TRACKED; } @@ -659,8 +707,10 @@ void wbt_disable_default(struct request_queue *q) { struct rq_wb *rwb = q->rq_wb; - if (rwb && rwb->enable_state == WBT_STATE_ON_DEFAULT) + if (rwb && rwb->enable_state == WBT_STATE_ON_DEFAULT) { + blk_stat_deactivate(rwb->cb); wbt_exit(q); + } } EXPORT_SYMBOL_GPL(wbt_disable_default); @@ -697,7 +747,15 @@ u64 wbt_default_latency_nsec(struct request_queue *q) static int wbt_data_dir(const struct request *rq) { - return rq_data_dir(rq); + const int op = req_op(rq); + + if (op == REQ_OP_READ) + return READ; + else if (op == REQ_OP_WRITE || op == REQ_OP_FLUSH) + return WRITE; + + /* don't account */ + return -1; } int wbt_init(struct request_queue *q)