diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index aad0f39..f5e41cc 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1808,9 +1808,209 @@ out_unlock: goto out; } +#include +struct bio * mpage_alloc(struct block_device *bdev, sector_t first_sector, + int nr_vecs, gfp_t gfp_flags); +struct bio *mpage_bio_submit(int rw, struct bio *bio); + + +struct ext3_readpage_state { + struct inode *inode; + int offsets[4]; + int depth; + int blocks_to_boundary; + int cur_depth; + int cur_block; + int waiting_on_lock; + struct buffer_head *cur_bh; + struct bio *bio; + struct wait_bit_queue wait_bit; + struct work_struct work; +}; + +#if 0 +static int ext3_async_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + BUG_ON(create); +} +#endif +#define dprintk(x...) do { ; } while (0) + +static void ext3_readpage_statemachine(struct ext3_readpage_state *state); +static void ext3_readpage_work_func(struct work_struct *work) +{ + struct ext3_readpage_state *state; + + state = container_of(work, struct ext3_readpage_state, work); + dprintk("ext3_readpage_work_func(%p): state=%p\n", work, state); + ext3_readpage_statemachine(state); +} + +static int ext3_readpage_wait_func(wait_queue_t *wait, unsigned mode, int flags, + void *arg) +{ + struct ext3_readpage_state *state = wait->private; + struct wait_bit_key *key = arg; + + //pginfo = container_of(wait, struct ext3_readpage_state, wait_bit.wait); + dprintk("ext3_readpage_wait_func: state=%p\n", state); + // state, pginfo, idx); + //dprintk("key->flags=%p key->bit_nr=%d, page->flags=%p\n", + // key->flags, key->bit_nr, &pginfo->page->flags); + if (state->wait_bit.key.flags != key->flags || + state->wait_bit.key.bit_nr != key->bit_nr || + test_bit(key->bit_nr, key->flags)) + return 0; + dprintk("ext3_readpage_wait_func: page is unlocked/uptodate\n"); + list_del_init(&wait->task_list); + INIT_WORK(&state->work, ext3_readpage_work_func); + schedule_work(&state->work); + return 1; +} + +static void ext3_readpage_wait_on_bh(struct ext3_readpage_state *state) +{ + wait_queue_head_t *wq; + unsigned long flags; + int ret = 0; + + state->wait_bit.key.flags = &state->cur_bh->b_state; + state->wait_bit.key.bit_nr = BH_Lock; + state->wait_bit.wait.private = state; + state->wait_bit.wait.func = ext3_readpage_wait_func; + + wq = bit_waitqueue(state->wait_bit.key.flags, + state->wait_bit.key.bit_nr); + spin_lock_irqsave(&wq->lock, flags); + __add_wait_queue(wq, &state->wait_bit.wait); + if (!buffer_locked(state->cur_bh)) { + dprintk("ext3_readpage_wait_on_bh(%p): buffer not locked\n", state); + list_del_init(&state->wait_bit.wait.task_list); + ret = 1; + } + spin_unlock_irqrestore(&wq->lock, flags); + + dprintk("ext3_readpage_wait_on_bh(%p): ret=%d\n", state, ret); + if (ret) + ext3_readpage_statemachine(state); +} + +static void ext3_readpage_statemachine(struct ext3_readpage_state *state) +{ + struct ext3_inode_info *ei = EXT3_I(state->inode); + struct buffer_head *bh; + int offset; + __le32 *blkp; + u32 blocknr; + + dprintk("ext3_readpage_statemachine(%p): cur_depth=%d\n", + state, state->cur_depth); + + if (state->waiting_on_lock) + goto lock_buffer; + + offset = state->offsets[state->cur_depth]; + if (state->cur_depth == 0) + blkp = ei->i_data + offset; + else + blkp = (__le32 *)state->cur_bh->b_data + offset; + + blocknr = le32_to_cpu(*blkp); + if (state->cur_bh) + brelse(state->cur_bh); + state->cur_depth++; + + dprintk("state->cur_depth=%d depth=%d offset=%d blocknr=%u\n", + state->cur_depth, state->depth, offset, blocknr); + if (state->cur_depth == state->depth) { + dprintk("submitting bio %p for block %u\n", state->bio, blocknr); + state->bio->bi_sector = (sector_t)blocknr << (state->inode->i_blkbits - 9); + mpage_bio_submit(READ, state->bio); + return; + } + + //state->cur_bh = ext3_getblk(NULL, state->inode, blocknr, 0, &err); + state->cur_bh = sb_getblk(state->inode->i_sb, blocknr); + if (!state->cur_bh) { + printk("sb_getblk(%p, %u) failed\n", + state->inode->i_sb, blocknr); + printk("FAIL!\n"); + return; //FIXME + } + + state->cur_bh->b_end_io = end_buffer_read_sync; + dprintk("ext3_readpage_statemachine: cur_bh=%p\n", state->cur_bh); + +lock_buffer: + state->waiting_on_lock = 0; + if (buffer_uptodate(state->cur_bh)) { + dprintk("ext3_readpage_statemachine(%p): buffer uptodate\n", + state); + return ext3_readpage_statemachine(state); + } + + dprintk("ext3_readpage_statemachine(%p): locking buffer\n", state); + + if (!trylock_buffer(state->cur_bh)) { + state->waiting_on_lock = 1; + ext3_readpage_wait_on_bh(state); + return; + } + + /* We have the buffer locked */ + if (buffer_uptodate(state->cur_bh)) { + dprintk("ext3_readpage_statemachine: buffer uptodate after lock\n"); + unlock_buffer(state->cur_bh); + return ext3_readpage_statemachine(state); + } + + bh = state->cur_bh; + get_bh(bh); + ext3_readpage_wait_on_bh(state); + submit_bh(READ | REQ_META | REQ_PRIO, bh); +} + + +static int ext3_async_readpage(struct file *file, struct page *page) +{ + struct ext3_readpage_state *state = page_address(page); + struct inode *inode = file->f_mapping->host; + sector_t iblock; + + memset(state, 0, sizeof(*state)); + state->inode = inode; + + iblock = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + state->depth = ext3_block_to_path(state->inode, iblock, state->offsets, + &state->blocks_to_boundary); + dprintk("inode[%lu] page[%Lu]: depth=%d, offsets=%d,%d,%d,%d\n", + inode->i_ino, (unsigned long long)page->index, state->depth, + state->offsets[0], state->offsets[1], state->offsets[2], + state->offsets[3]); + if (state->depth == 1) + return mpage_readpage(page, ext3_get_block); + + state->bio = mpage_alloc(inode->i_sb->s_bdev, 0, 1, GFP_NOFS); + if (!state->bio) { + printk("ext3_async_readpage(%p, %Lu): mpage_alloc failed\n", + file, (unsigned long long)iblock); + unlock_page(page); + return -ENOMEM; + } + bio_add_page(state->bio, page, PAGE_SIZE, 0); + ext3_readpage_statemachine(state); + return 0; +} + static int ext3_readpage(struct file *file, struct page *page) { + struct inode *inode = file->f_mapping->host; trace_ext3_readpage(page); + + if (test_opt(inode->i_sb, NOORLOV)) + return ext3_async_readpage(file, page); + return mpage_readpage(page, ext3_get_block); } @@ -1970,7 +2170,7 @@ static int ext3_journalled_set_page_dirty(struct page *page) static const struct address_space_operations ext3_ordered_aops = { .readpage = ext3_readpage, - .readpages = ext3_readpages, + //.readpages = ext3_readpages, .writepage = ext3_ordered_writepage, .write_begin = ext3_write_begin, .write_end = ext3_ordered_write_end, @@ -1985,7 +2185,7 @@ static const struct address_space_operations ext3_ordered_aops = { static const struct address_space_operations ext3_writeback_aops = { .readpage = ext3_readpage, - .readpages = ext3_readpages, + //.readpages = ext3_readpages, .writepage = ext3_writeback_writepage, .write_begin = ext3_write_begin, .write_end = ext3_writeback_write_end, @@ -2000,7 +2200,7 @@ static const struct address_space_operations ext3_writeback_aops = { static const struct address_space_operations ext3_journalled_aops = { .readpage = ext3_readpage, - .readpages = ext3_readpages, + //.readpages = ext3_readpages, .writepage = ext3_journalled_writepage, .write_begin = ext3_write_begin, .write_end = ext3_journalled_write_end,