From 09a378da01684e1a051fd774bc222aa6565dc26c Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Wed, 15 Feb 2012 23:22:10 +0100 Subject: [PATCH 82/84] lsbd: separate deadline scheduling This patch adds separate deadline scheduling required to properly support sector moving. The new code is simpler and fixes problems with requests that need two separate schedulings - deadline to guarantee that this sector will be written before old version of this sector and normal scheduling, needed to not delay such request. Signed-off-by: Krzysztof Mazur --- drivers/block/lsbd.c | 119 +++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index b86759b..a46b965 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -56,9 +56,9 @@ struct __block_test { * Write queue priorities */ enum { - LSBD_QUEUE_MOVE, /* rewrite of old data */ LSBD_QUEUE_HIGH, /* high priority requests */ LSBD_QUEUE_NORMAL, /* normal requests */ + LSBD_QUEUE_MOVE, /* only when I/O */ LSBD_QUEUE_COUNT, /* must be last */ }; @@ -129,6 +129,10 @@ struct lsbd { unsigned int cur_lcache; spinlock_t wqueue_lock; + + struct list_head deadline_queue; + unsigned int deadline_qlen; + struct list_head wqueue[LSBD_QUEUE_COUNT]; unsigned int wqueue_qlen[LSBD_QUEUE_COUNT]; struct list_head wqueue_hash[LSBD_QUEUE_HASH_SIZE]; @@ -147,6 +151,7 @@ struct lsbd { struct lsbd_request { struct buffer_head *bh; struct list_head list; + struct list_head deadline_list; struct list_head hash_list; unsigned int sector; unsigned int queue; @@ -1623,6 +1628,10 @@ static int __lsbd_want_write(struct lsbd *p) static void __lsbd_req_remove(struct lsbd *p, struct lsbd_request *r) { + if (r->deadline != LSBD_NO_DEADLINE) { + list_del(&r->deadline_list); + p->deadline_qlen--; + } list_del(&r->list); list_del(&r->hash_list); p->wqueue_qlen[r->queue]--; @@ -1644,6 +1653,7 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, int need_wakeup = 0; int move = (prio == LSBD_QUEUE_MOVE); unsigned int hash = lsbd_hash(sector); + int deadline = move; if (prio >= LSBD_QUEUE_COUNT) return -EINVAL; @@ -1665,9 +1675,8 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, r->bh = bh; r->sector = sector; r->move = move; + r->deadline = LSBD_NO_DEADLINE; - if (move) - r->deadline = block_sub(p, p->clean_block, 2); spin_lock_irqsave(&p->wqueue_lock, flags); list_for_each_entry(r2, &p->wqueue_hash[hash], hash_list) { @@ -1680,20 +1689,26 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, */ p->queue_move_skip++; - /* - * to avoid losing both versions of this - * sector we need to use higher priority - * to pending write request - */ - list_del(&r2->list); - r2->queue = LSBD_QUEUE_MOVE; + BUG_ON(r2->deadline != LSBD_NO_DEADLINE); r2->deadline = block_sub(p, p->clean_block, 2); - list_add_tail(&r2->list, - &p->wqueue[r2->queue]); + list_add_tail(&r2->deadline_list, + &p->deadline_queue); + p->deadline_qlen++; kfree(r); goto out_unlock; } else { BUG_ON(!r2->move); + BUG_ON(r2->deadline == LSBD_NO_DEADLINE); + + /* + * add current request in the same place + * as old, use the same deadline + */ + r->deadline = r2->deadline; + list_add(&r->deadline_list, + &r2->deadline_list); + p->deadline_qlen++; + /* remove pending rewrite request */ __lsbd_req_remove(p, r2); p->queue_move_drop++; @@ -1703,17 +1718,17 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, lsbd_put_buffer(p, r2->bh); r2->bh = NULL; kfree(r2); - - /* - * to avoid losing both versions of this - * sector we need to use higher priority - * to pending write request - */ - prio = LSBD_QUEUE_MOVE; break; } } } + + if (move) { + r->deadline = block_sub(p, p->clean_block, 2); + list_add_tail(&r->deadline_list, &p->deadline_queue); + p->deadline_qlen++; + } + r->queue = prio; list_add_tail(&r->list, &p->wqueue[prio]); list_add_tail(&r->hash_list, &p->wqueue_hash[hash]); @@ -2262,9 +2277,8 @@ static int lsbd_block_after_eq(struct lsbd *p, unsigned int a, unsigned int b) static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, unsigned int block, int throttle) { - struct lsbd_request *ret = NULL; - struct lsbd_request *r; - unsigned int i; + struct lsbd_request *r = NULL; + unsigned int queue; unsigned long flags; unsigned int clean_blocks; unsigned int data_sects; @@ -2274,52 +2288,42 @@ static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, spin_lock_irqsave(&p->wqueue_lock, flags); - for (i = 0; i <= LSBD_QUEUE_COUNT; i++) { - unsigned int queue; - - queue = i; - if (queue == LSBD_QUEUE_COUNT) - queue = LSBD_QUEUE_MOVE; + /* deadline scheduling */ + if (!list_empty(&p->deadline_queue)) { + r = list_entry(p->deadline_queue.next, struct lsbd_request, + deadline_list); + if (lsbd_block_after_eq(p, block, r->deadline)) + goto out_unlock; + if (clean_blocks < LSBD_CLEAN_LOW) + goto out_unlock; + r = NULL; + } + for (queue = 0; queue < LSBD_QUEUE_COUNT; queue++) { if (list_empty(&p->wqueue[queue])) continue; r = list_entry(p->wqueue[queue].next, struct lsbd_request, list); - /* use strict order when the number of clean blocks is low */ - if (clean_blocks < LSBD_CLEAN_LOW) { - ret = r; - break; - } - - if (i == LSBD_QUEUE_MOVE) { - /* meet deadlines for LSBD_QUEU_MOVE requests */ - if (lsbd_block_after_eq(p, block, r->deadline)) { - ret = r; - break; - } - continue; - } - /* never throttle high prority requests */ - if (i == LSBD_QUEUE_HIGH) { - ret = r; + if (queue == LSBD_QUEUE_HIGH) break; - } - + if (throttle) { /* add some randomness, use throtting only in 50% cases */ - if (!(rand() & 0x40000000)) + if (!(rand() & 0x40000000)) { + r = NULL; break; + } } - ret = r; break; } - if (ret != NULL) - __lsbd_req_remove(p, ret); +out_unlock: + if (r != NULL) + __lsbd_req_remove(p, r); spin_unlock_irqrestore(&p->wqueue_lock, flags); - return ret; + return r; } /** @@ -2736,11 +2740,12 @@ static int lsbd_seq_show(struct seq_file *seq, void *v) p->sectors_written, p->blocks_written); seq_printf(seq, " move: %ld %ld\n", p->queue_move_drop, p->queue_move_skip); - seq_printf(seq, " qlen: %d %d %d %d\n", - p->wqueue_qlen[LSBD_QUEUE_MOVE], + seq_printf(seq, " qlen: h: %d n: %d m: %d t: %d d: %d\n", p->wqueue_qlen[LSBD_QUEUE_HIGH], p->wqueue_qlen[LSBD_QUEUE_NORMAL], - p->wqueue_len); + p->wqueue_qlen[LSBD_QUEUE_MOVE], + p->wqueue_len, + p->deadline_qlen); for (i = 0; i < p->sectors_per_block; i++) seq_printf(seq, " hist[%3d]: %7d %7d %7d %7d %7d %7d\n", i, @@ -2795,6 +2800,10 @@ int __init lsbd_init_module(void) INIT_LIST_HEAD(&p->wqueue[j]); p->wqueue_qlen[j] = 0; } + + INIT_LIST_HEAD(&p->deadline_queue); + p->deadline_qlen = 0; + for (j = 0; j < LSBD_QUEUE_HASH_SIZE; j++) INIT_LIST_HEAD(&p->wqueue_hash[j]); -- 1.8.4.652.g0d6e0ce