From e8b311ffd424d8bfcb8f93c58744b19f6a2e9314 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Tue, 21 Dec 2010 23:37:12 +0100 Subject: [PATCH 12/84] lsbd: fix buffer allocation from lsbd_thread lsbd_thread should not use getblk() and bread() because they can call write_some_buffers() and call lsbd_make_request(). Now very simple allocator from loop device is used. This version works much better under high memory pressure, cat /dev/zero > /dev/lsbd0 now works without problems. --- drivers/block/lsbd.c | 162 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 137 insertions(+), 25 deletions(-) diff --git a/drivers/block/lsbd.c b/drivers/block/lsbd.c index 45e2292..5933814 100644 --- a/drivers/block/lsbd.c +++ b/drivers/block/lsbd.c @@ -237,25 +237,135 @@ static unsigned int lsbd_block_size(struct lsbd *p) * @size: number of bytes to read * * This function NULL on failure; pointer to allocated buffer otherwise. + * + * This function cannot be called from lsbd_thread. */ struct buffer_head *lsbd_bread(struct lsbd *p, int block, int size) { return bread(p->dev, block * p->sectors_per_block, size); } +static struct buffer_head *lsbd_get_buffer(struct lsbd *p) +{ + struct buffer_head *bh; + + do { + bh = kmem_cache_alloc(bh_cachep, SLAB_NOIO); + if (bh) + break; + + run_task_queue(&tq_disk); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + } while (1); + memset(bh, 0, sizeof(*bh)); + + bh->b_size = p->sector_size; + bh->b_dev = p->dev; + bh->b_state = (1 << BH_Req) | (1 << BH_Mapped); + + /* + * easy way out, although it does waste some memory for < PAGE_SIZE + * blocks... if highmem bounce buffering can get away with it, + * so can we :-) + */ + do { + bh->b_page = alloc_page(GFP_NOIO); + if (bh->b_page) + break; + + run_task_queue(&tq_disk); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + } while (1); + + bh->b_data = page_address(bh->b_page); + init_waitqueue_head(&bh->b_wait); + bh->b_rdev = p->dev; + atomic_set(&bh->b_count, 1); + + lsbd_debug(p, "allocated buffer %p\n", bh); + return bh; +} + +static void lsbd_put_buffer(struct lsbd *p, struct buffer_head *bh) +{ + if (bh) { + __free_page(bh->b_page); + kmem_cache_free(bh_cachep, bh); + lsbd_debug(p, "free buffer %p\n", bh); + } +} + /** - * lsbs_sread - read data sector + * lsbs_getblk - allocate LSBD buffer head + * @p: LSBD device + * @block: block number + * @sector: sector offset in block + * + * This function NULL on failure; pointer to allocated buffer otherwise. + */ +struct buffer_head *lsbd_getblk(struct lsbd *p, int block, unsigned int sector) +{ + struct buffer_head *bh; + + bh = lsbd_get_buffer(p); + bh->b_blocknr = block * p->sectors_per_block + sector; + bh->b_rsector = (block * p->sectors_per_block + sector) + * (p->sector_size >> 9); + return bh; +} + +/* + * Default synchronous end-of-IO handler.. Just mark it up-to-date and + * unlock the buffer. This is what ll_rw_block uses too. + */ +void lsbd_end_buffer_io_sync(struct buffer_head *bh, int uptodate) +{ + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + put_bh(bh); +} + +/** + * lsbs_sread - read data * @p: LSBD device * @block: block number * @sector: sector offset in block - * @size: number of bytes to read * * This function NULL on failure; pointer to allocated buffer otherwise. */ -struct buffer_head *lsbd_sread(struct lsbd *p, int block, unsigned int sector, - int size) +struct buffer_head *lsbd_sread(struct lsbd *p, int block, unsigned int sector) { - return bread(p->dev, block * p->sectors_per_block + sector + 1, size); + struct buffer_head *bh; + + lsbd_debug(p, "sector %d.%d: getblk\n", block, sector); + bh = lsbd_getblk(p, block, sector); + if (buffer_uptodate(bh)) { + lsbd_debug(p, "sector %d.%d: already uptodate\n", + block, sector); + return bh; + } + set_bit(BH_Sync, &bh->b_state); + lsbd_debug(p, "sector %d.%d: read\n", block, sector); + + lock_buffer(bh); + bh->b_end_io = lsbd_end_buffer_io_sync; + set_bit(BH_Req, &bh->b_state); + set_bit(BH_Launder, &bh->b_state); + get_bh(bh); + generic_make_request(READ, bh); + + lsbd_debug(p, "sector %d.%d: wait...\n", block, sector); + wait_on_buffer(bh); + if (buffer_uptodate(bh)) { + lsbd_debug(p, "sector %d.%d: ok\n", block, sector); + return bh; + } + lsbd_debug(p, "sector %d.%d: I/O error\n", block, sector); + brelse(bh); + lsbd_put_buffer(p, bh); + return NULL; } /** @@ -337,7 +447,7 @@ static int lsbd_find_current_block(struct lsbd *p) */ for (i = 0; i < p->blocks; i++) { reads++; - bh = lsbd_bread(p, i, sizeof(*b)); + bh = lsbd_bread(p, i, p->sector_size); if (bh == NULL) continue; @@ -413,7 +523,7 @@ static int lsbd_find_current_block(struct lsbd *p) p->cur_block, p->epoch, reads); return 0; } - + /** * lsbd_psector_valid - check if physical sector identifier is valid * @p: LSBD device @@ -1031,13 +1141,9 @@ static int lsbd_queue_bh(struct lsbd *p, struct buffer_head *bh, return -EINVAL; if (!move) { - if (current == p->tsk) { - lsbd_info(p, "%s: BUG?\n", current->comm); - dump_stack(); - } else { - lsbd_info(p, "%s: waiting on request\n", current->comm); - down(&p->req_sem); - } + BUG_ON(current == p->tsk); + lsbd_debug(p, "%s: waiting on request\n", current->comm); + down(&p->req_sem); } r = kmalloc(sizeof(*r), GFP_NOIO); @@ -1170,7 +1276,7 @@ void lsbd_clean_block(struct lsbd *p) if (block >= p->blocks) block = 0; - bh = lsbd_bread(p, block, p->sector_size); + bh = lsbd_sread(p, block, 0); if (bh == NULL) goto out; b = (void *) bh->b_data; @@ -1200,7 +1306,7 @@ void lsbd_clean_block(struct lsbd *p) if (sector_id >= p->lsectors) continue; if (p->lcache[sector_id] == s) { - rbh = lsbd_sread(p, block, i, p->sector_size); + rbh = lsbd_sread(p, block, i + 1); if (rbh == NULL) continue; @@ -1209,10 +1315,12 @@ void lsbd_clean_block(struct lsbd *p) if (ret) { unlock_buffer(rbh); brelse(rbh); + lsbd_put_buffer(p, rbh); } } } brelse(bh); + lsbd_put_buffer(p, bh); out: p->clean_block = block; @@ -1319,7 +1427,6 @@ static int lsbd_write_block(struct lsbd *p) struct lsbd_block *b; struct lsbd_sect *sects; struct lsbd_request *r; - unsigned int base_sector; unsigned int i; p->cur_block++; @@ -1338,11 +1445,8 @@ static int lsbd_write_block(struct lsbd *p) return -ENOMEM; } - base_sector = p->cur_block * p->sectors_per_block; for (i = 0; i < p->sectors_per_block; i++) { - lsbd_debug(p, "sector: %d, block: %d\n", base_sector + i, - (base_sector + i)); - bh[i] = getblk(p->dev, base_sector + i, p->sector_size); + bh[i] = lsbd_getblk(p, p->cur_block, i); BUG_ON(bh[i] == NULL); lock_buffer(bh[i]); } @@ -1376,6 +1480,7 @@ static int lsbd_write_block(struct lsbd *p) if (move) { unlock_buffer(r->bh); brelse(r->bh); + lsbd_put_buffer(p, r->bh); r->bh = NULL; } wbh[i + 1] = r->bh; @@ -1398,15 +1503,20 @@ static int lsbd_write_block(struct lsbd *p) lsbd_block_commit(p, b); for (i = 0; i < p->sectors_per_block; i++) { mark_buffer_uptodate(bh[i], 1); - unlock_buffer(bh[i]); - mark_buffer_dirty(bh[i]); +// unlock_buffer(bh[i]); +// mark_buffer_dirty(bh[i]); + bh[i]->b_end_io = lsbd_end_buffer_io_sync; + set_bit(BH_Req, &bh[i]->b_state); + set_bit(BH_Launder, &bh[i]->b_state); + get_bh(bh[i]); + generic_make_request(WRITE, bh[i]); } - ll_rw_block(WRITE, p->sectors_per_block, bh); lsbd_debug(p, "waiting for write\n"); for (i = 0; i < p->sectors_per_block; i++) { wait_on_buffer(bh[i]); brelse(bh[i]); + lsbd_put_buffer(p, bh[i]); if (wbh[i] != NULL) wbh[i]->b_end_io(wbh[i], 1); @@ -1466,7 +1576,9 @@ static int lsbd_thread(void *data) current->state = TASK_RUNNING; remove_wait_queue(&p->wqueue_wait, &wait); - lsbd_debug(p, "lsbd_thread: reqs %d\n", len); + lsbd_debug(p, "lsbd_thread: %s %s\n", + want_write ? "want_write" : "", + want_clean ? "want_clean" : ""); if (signal_pending(current)) { spin_lock(¤t->sigmask_lock); -- 1.8.4.652.g0d6e0ce