From 7b4124fabe81ae35468f50a86c6cfe48e35b820b Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Tue, 14 Feb 2012 16:01:31 +0100 Subject: [PATCH 72/84] lsbd: resolve rewrite/wqueue conflicts This patch adds tracking of locical sectors in write queue. The conflicts with rewrite are not resolved. This allows for safe delaying rewrite requests. Signed-off-by: Krzysztof Mazur --- drivers/block/lsbd.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index dda760f..1f86572 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -62,6 +62,9 @@ enum { LSBD_QUEUE_COUNT, /* must be last */ }; +#define LSBD_QUEUE_HASH_SHIFT 8 +#define LSBD_QUEUE_HASH_SIZE (1 << LSBD_QUEUE_HASH_SHIFT) + #define LSBD_CLEAN_HIGH 256 /* LSBD_CLEAN_WINDOW defines the wanted minimum number of clean blocks */ @@ -104,6 +107,8 @@ struct lsbd { unsigned long sectors_written; unsigned long blocks_written; + unsigned long queue_move_drop; + unsigned long queue_move_skip; rwlock_t lcache_lock; lsbd_lcache_t *lcache; @@ -112,6 +117,7 @@ struct lsbd { spinlock_t wqueue_lock; struct list_head wqueue[LSBD_QUEUE_COUNT]; unsigned int wqueue_qlen[LSBD_QUEUE_COUNT]; + struct list_head wqueue_hash[LSBD_QUEUE_HASH_SIZE]; wait_queue_head_t wqueue_wait; unsigned int wqueue_len; unsigned int blocks_to_write; @@ -127,8 +133,10 @@ struct lsbd { struct lsbd_request { struct buffer_head *bh; struct list_head list; + struct list_head hash_list; unsigned int sector; int move; + unsigned int queue; }; #if 0 @@ -164,6 +172,15 @@ static unsigned int block_prev(struct lsbd *p, unsigned int block) return block ? block - 1 : p->blocks - 1; } +static unsigned int lsbd_hash(unsigned int block) +{ + unsigned int hash; + + hash = block + (block << 6) + (block << 12); + + return hash & (LSBD_QUEUE_HASH_SIZE - 1); +} + /** * block_diff - compute distance between two blocks * @p: LSBD device @@ -1564,6 +1581,14 @@ static int __lsbd_want_write(struct lsbd *p) { return p->wqueue_len - p->wqueue_qlen[LSBD_QUEUE_MOVE]; } + +static void __lsbd_req_remove(struct lsbd *p, struct lsbd_request *r) +{ + list_del(&r->list); + list_del(&r->hash_list); + p->wqueue_qlen[r->queue]--; + p->wqueue_len--; +} /** * lsbd_queue_bh - queue write operation @@ -1576,9 +1601,10 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, unsigned int sector, unsigned int prio) { unsigned long flags; - struct lsbd_request *r; + struct lsbd_request *r, *r2; int need_wakeup = 0; int move = (prio == LSBD_QUEUE_MOVE); + unsigned int hash = lsbd_hash(sector); if (prio >= LSBD_QUEUE_COUNT) return -EINVAL; @@ -1600,13 +1626,36 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, r->bh = bh; r->sector = sector; r->move = move; + r->queue = prio; spin_lock_irqsave(&p->wqueue_lock, flags); + list_for_each_entry(r2, &p->wqueue_hash[hash], hash_list) { + if (r2->sector == sector) { + if (r->move) { + BUG_ON(r2->move); + /* + * new version is already in write queue, + * just drop this rewrite request + */ + p->queue_move_skip++; + kfree(r); + goto out_unlock; + } else { + BUG_ON(!r2->move); + /* remove pending rewrite request */ + __lsbd_req_remove(p, r2); + p->queue_move_drop = 0; + } + } + } list_add_tail(&r->list, &p->wqueue[prio]); + list_add_tail(&r->hash_list, &p->wqueue_hash[hash]); + /* TODO */ p->wqueue_qlen[prio]++; if (!__lsbd_want_write(p)) need_wakeup = 1; p->wqueue_len++; +out_unlock: spin_unlock_irqrestore(&p->wqueue_lock, flags); if (need_wakeup) wake_up_interruptible(&p->wqueue_wait); @@ -2176,9 +2225,7 @@ static struct lsbd_request *lsbd_request_dequeue(struct lsbd *p, int throttle) if (!list_empty(&p->wqueue[i])) { r = list_entry(p->wqueue[i].next, struct lsbd_request, list); - list_del(&r->list); - p->wqueue_qlen[i]--; - p->wqueue_len--; + __lsbd_req_remove(p, r); break; } } @@ -2580,6 +2627,8 @@ static int lsbd_seq_show(struct seq_file *seq, void *v) seq_printf(seq, " write: %d %d %lld %ld %ld\n", p->cur_block, p->clean_block, p->epoch, p->sectors_written, p->blocks_written); + seq_printf(seq, " move: %ld %ld\n", + p->queue_move_drop, p->queue_move_skip); seq_printf(seq, " status: %s ( %ld %ld %ld )\n", lsbd_status(p), p->read_errors, p->uncorrectable, p->write_errors); @@ -2624,12 +2673,16 @@ int __init lsbd_init_module(void) INIT_LIST_HEAD(&p->wqueue[j]); p->wqueue_qlen[j] = 0; } + for (j = 0; j < LSBD_QUEUE_HASH_SIZE; j++) + INIT_LIST_HEAD(&p->wqueue_hash[j]); p->id = i; spin_lock_init(&p->lock); spin_lock_init(&p->wqueue_lock); rwlock_init(&p->lcache_lock); p->wqueue_len = 0; + p->queue_move_drop = 0; + p->queue_move_skip = 0; init_waitqueue_head(&p->wqueue_wait); init_MUTEX(&p->mutex); sema_init(&p->req_sem, 512); -- 1.8.4.652.g0d6e0ce