From d3069073c75055602d6574115312c15e94856fa6 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Wed, 15 Feb 2012 20:14:23 +0100 Subject: [PATCH 78/84] lsbd: deadline-based rewrite scheduling Since introduction of delayed rewrite requests in some cases the sector can be written after overwrite of original sector. This patch fixes this problem by storing deadline in all rewrite request. This also significantly simplifies rewrite scheduling. Signed-off-by: Krzysztof Mazur --- drivers/block/lsbd.c | 112 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index 2248688..b9da2df 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -62,6 +62,8 @@ enum { LSBD_QUEUE_COUNT, /* must be last */ }; +#define LSBD_NO_DEADLINE ((unsigned int) -1) + #define LSBD_QUEUE_HASH_SHIFT 8 #define LSBD_QUEUE_HASH_SIZE (1 << LSBD_QUEUE_HASH_SHIFT) @@ -135,8 +137,9 @@ struct lsbd_request { struct list_head list; struct list_head hash_list; unsigned int sector; - int move; unsigned int queue; + int move; + unsigned int deadline; }; #if 0 @@ -172,6 +175,22 @@ static unsigned int block_prev(struct lsbd *p, unsigned int block) return block ? block - 1 : p->blocks - 1; } +static unsigned int __block_sub(struct lsbd *p, unsigned int block, + unsigned int a) +{ + if (block > a) + return block - a; + return p->blocks - a + block; +} + +static unsigned int block_sub(struct lsbd *p, unsigned int block, + unsigned int a) +{ + if (p->mirrored) + a <<= 1; + return __block_sub(p, block, a); +} + static unsigned int lsbd_hash(unsigned int block) { unsigned int hash; @@ -1626,6 +1645,9 @@ 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) { @@ -1645,6 +1667,7 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, */ list_del(&r2->list); r2->queue = LSBD_QUEUE_MOVE; + r2->deadline = block_sub(p, p->clean_block, 2); list_add_tail(&r2->list, &p->wqueue[r2->queue]); kfree(r); @@ -2185,6 +2208,15 @@ static unsigned int rand(void) return rand_seed; } +static int lsbd_block_after_eq(struct lsbd *p, unsigned int a, unsigned int b) +{ + if (b > a) { + return (b - a) << 1 >= p->blocks; + } else { + return p->blocks >= (a - b) << 1; + } +} + /** * lsbd_request_dequeue - dequeue request * @p: LSBD device @@ -2200,24 +2232,20 @@ static unsigned int rand(void) * * NORMAL - all other write operations */ -static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, int throttle) +static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, + unsigned int block, int throttle) { - struct lsbd_request *r = NULL; + struct lsbd_request *ret = NULL; + struct lsbd_request *r; unsigned int i; unsigned long flags; unsigned int clean_blocks; - unsigned int min_clean_sects; unsigned int data_sects; - int can_throttle; clean_blocks = lsbd_clean_blocks(p); data_sects = p->sectors_per_block - 1; spin_lock_irqsave(&p->wqueue_lock, flags); - min_clean_sects = p->wqueue_qlen[LSBD_QUEUE_MOVE]; - - /* reserve */ - min_clean_sects += LSBD_CLEAN_LOW * data_sects; for (i = 0; i <= LSBD_QUEUE_COUNT; i++) { unsigned int queue; @@ -2226,51 +2254,43 @@ static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, int throttle) if (queue == LSBD_QUEUE_COUNT) queue = LSBD_QUEUE_MOVE; - can_throttle = 1; - /* never throttle below LSBD_CLEAN_LOW clean blocks */ - if (clean_blocks < LSBD_CLEAN_LOW) - can_throttle = 0; + if (list_empty(&p->wqueue[queue])) + continue; - /* never throttle when all sectors are needed for move */ - if (min_clean_sects >= clean_blocks * data_sects) - can_throttle = 0; + r = list_entry(p->wqueue[queue].next, struct lsbd_request, + list); - /* - * if handing of LSBD_QUEUE_MOVE queue is not required - * it's better to delay those requests. - */ - if ((i == LSBD_QUEUE_MOVE) && can_throttle) { - if (p->wqueue_qlen[LSBD_QUEUE_HIGH] > 0) - continue; - if (p->wqueue_qlen[LSBD_QUEUE_NORMAL] > 0) - continue; + /* use strict order when the number of clean blocks is low */ + if (clean_blocks < LSBD_CLEAN_LOW) { + ret = r; + break; } - /* never throttle when there are high-priority requests */ - if (p->wqueue_qlen[LSBD_QUEUE_HIGH]) - can_throttle = 0; - - /* add some randomness, use throtting only in 50% cases */ - if (!(rand() & 0x40000000)) - can_throttle = 0; - - /* - * High priority requests should be never throttled - * to minimize write latency. Currently it is also - * a good idea to not throttle move requests. - */ - if (throttle && can_throttle) - 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; + } - if (!list_empty(&p->wqueue[queue])) { - r = list_entry(p->wqueue[queue].next, struct lsbd_request, - list); - __lsbd_req_remove(p, r); + /* never throttle high prority requests */ + if (i == LSBD_QUEUE_HIGH) { + ret = r; break; } + + if (throttle) { + /* add some randomness, use throtting only in 50% cases */ + if (!(rand() & 0x40000000)) + break; + } } + if (ret != NULL) + __lsbd_req_remove(p, ret); spin_unlock_irqrestore(&p->wqueue_lock, flags); - return r; + return ret; } /** @@ -2362,7 +2382,7 @@ static int lsbd_write_block(struct lsbd *p) if (i >= p->sectors_per_block - 2) throttle = 1; - r = lsbd_request_dequeue(p, throttle); + r = lsbd_request_dequeue(p, p->cur_block, throttle); if (r != NULL) { int move; u32 cksum; -- 1.8.4.652.g0d6e0ce