From e4cf509d402bfddf58dfc42cd85bd58abac11805 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Tue, 15 Feb 2011 21:03:48 +0100 Subject: [PATCH 18/84] lsbd: some cleanups --- drivers/block/lsbd.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index e72618f..318b16a 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -58,6 +58,15 @@ enum { LSBD_QUEUE_COUNT, /* must be last */ }; +/* LSBD_CLEAN_WINDOW defines the wanted minimum number of clean blocks */ +#define LSBD_CLEAN_WINDOW 16 + +/* number of blocks to clean in single pass */ +#define LSBD_CLEAN_BLOCKS_AT_ONCE 4 + +/* number of clean blocks when sector throttling is disabled */ +#define LSBD_CLEAN_LOW 4 + struct lsbd_partition { unsigned int start; unsigned int size; @@ -152,6 +161,29 @@ unsigned int block_diff(struct lsbd *p, unsigned int a, unsigned int b) } /** + * lsbd_clean_blocks - return number of cleaned blocks + * @p: LSBD device + * + * This function returns a number of cleaned blocks. This function + * should be called only from LSBD thread. + */ +static unsigned int lsbd_clean_blocks(struct lsbd *p) +{ + unsigned int clean_blocks; + + clean_blocks = block_diff(p, p->clean_block, p->cur_block); + + /* + * when mirroring is enabled effective number of cleaned blocks is + * two times smaller + */ + if (p->mirrored) + clean_blocks /= 2; + return clean_blocks; +} + + +/** * lsbd_map_sector - map sector in partition to global logical sector * @p: LSBD device * @partition: LSBD partition @@ -1590,15 +1622,38 @@ static int lsbd_write_lcache(struct lsbd *p, void *bp) /** * lsbd_request_dequeue - dequeue request * @p: LSBD device + * @throttle: may return NULL even if there are requests in queue + * + * This function implements sector write scheduling. It's very important + * for good write performance. We have three types of pending sectors: + * + * MOVE - these sectors were removed while cleaning blocks, they + * MUST be written before that block is overritten. + * + * HIGH - sectors written by realtime processes + * + * NORMAL - all other write operations */ -static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p) +static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, int throttle) { struct lsbd_request *r = NULL; unsigned int i; unsigned long flags; + unsigned int clean_blocks; + + clean_blocks = lsbd_clean_blocks(p); spin_lock_irqsave(&p->wqueue_lock, flags); for (i = 0; i < LSBD_QUEUE_COUNT; i++) { + /* + * High priority requests should be never throttled + * to minimize write latency. Currently it is also + * a good idea to not throttle move requests. + */ + if ((i == LSBD_QUEUE_NORMAL) && throttle + && (clean_blocks >= LSBD_CLEAN_LOW)) + break; + if (!list_empty(&p->wqueue[i])) { r = list_entry(p->wqueue[i].next, struct lsbd_request, list); @@ -1690,7 +1745,15 @@ static int lsbd_write_block(struct lsbd *p) wbh[0] = NULL; for (i = 0; i < p->sectors_per_block - 1; i++) { - r = lsbd_request_dequeue(p); + int throttle = 0; + + /* + * the last sector in block is used for throttling + */ + if (i >= p->sectors_per_block - 2) + throttle = 1; + + r = lsbd_request_dequeue(p, throttle); if (r != NULL) { int move; @@ -1783,7 +1846,6 @@ static int lsbd_thread(void *data) int want_write; int want_clean; unsigned int i; - unsigned int clean_window; lock_kernel(); daemonize(); @@ -1817,9 +1879,7 @@ static int lsbd_thread(void *data) if (p->wqueue_len || p->blocks_to_write) want_write = 1; spin_unlock_irq(&p->wqueue_lock); - clean_window = p->mirrored ? 32 : 16; - want_clean = (block_diff(p, p->clean_block, p->cur_block) - < clean_window); + want_clean = (lsbd_clean_blocks(p) < LSBD_CLEAN_WINDOW); if (!(want_write || want_clean)) schedule(); current->state = TASK_RUNNING; @@ -1845,12 +1905,16 @@ static int lsbd_thread(void *data) * into one. */ if (want_clean) { - for (i = 0; i < 4; i++) + unsigned int blocks = LSBD_CLEAN_BLOCKS_AT_ONCE; + + if (p->mirrored) + blocks *= 2; + + for (i = 0; i < blocks; i++) lsbd_clean_block(p); } - while (want_write && (block_diff(p, p->clean_block, - p->cur_block) > 1)) { + while (want_write && (lsbd_clean_blocks(p) > 1)) { ret = lsbd_write_block(p); spin_lock_irq(&p->wqueue_lock); if (!ret && p->blocks_to_write) -- 1.8.4.652.g0d6e0ce