X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=fs%2Fgfs2%2Fmeta_io.c;h=e62d4f620c5841c73b177f2022324b09442e1a13;hb=9cdd79c9b99873d600d397fda012fc3f57cc2776;hp=b85fa24646661335c343a4fbfa3f1438b28d5b1a;hpb=76467874b83835129dc454e3a7a8e5d1186101b0;p=linux-2.6-omap-h63xx.git diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index b85fa246466..e62d4f620c5 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -1,10 +1,10 @@ /* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. + * of the GNU General Public License version 2. */ #include @@ -17,11 +17,11 @@ #include #include #include +#include #include -#include +#include #include "gfs2.h" -#include "lm_interface.h" #include "incore.h" #include "glock.h" #include "glops.h" @@ -32,11 +32,7 @@ #include "rgrp.h" #include "trans.h" #include "util.h" - -#define buffer_busy(bh) \ -((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock) | (1ul << BH_Pinned))) -#define buffer_in_io(bh) \ -((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock))) +#include "ops_address.h" static int aspace_get_block(struct inode *inode, sector_t lblock, struct buffer_head *bh_result, int create) @@ -51,119 +47,9 @@ static int gfs2_aspace_writepage(struct page *page, return block_write_full_page(page, aspace_get_block, wbc); } -/** - * stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out. - * @bh: the buffer we're stuck on - * - */ - -static void stuck_releasepage(struct buffer_head *bh) -{ - struct gfs2_sbd *sdp = bh->b_page->mapping->host->i_sb->s_fs_info; - struct gfs2_bufdata *bd = bh->b_private; - struct gfs2_glock *gl; - - fs_warn(sdp, "stuck in gfs2_releasepage()\n"); - fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", - (uint64_t)bh->b_blocknr, atomic_read(&bh->b_count)); - fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); - fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); - - if (!bd) - return; - - gl = bd->bd_gl; - - fs_warn(sdp, "gl = (%u, %llu)\n", - gl->gl_name.ln_type, gl->gl_name.ln_number); - - fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", - (list_empty(&bd->bd_list_tr)) ? "no" : "yes", - (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); - - if (gl->gl_ops == &gfs2_inode_glops) { - struct gfs2_inode *ip = gl->gl_object; - unsigned int x; - - if (!ip) - return; - - fs_warn(sdp, "ip = %llu %llu\n", - ip->i_num.no_formal_ino, ip->i_num.no_addr); - fs_warn(sdp, "ip->i_count = %d, ip->i_vnode = %s\n", - atomic_read(&ip->i_count), - (ip->i_vnode) ? "!NULL" : "NULL"); - - for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) - fs_warn(sdp, "ip->i_cache[%u] = %s\n", - x, (ip->i_cache[x]) ? "!NULL" : "NULL"); - } -} - -/** - * gfs2_aspace_releasepage - free the metadata associated with a page - * @page: the page that's being released - * @gfp_mask: passed from Linux VFS, ignored by us - * - * Call try_to_free_buffers() if the buffers in this page can be - * released. - * - * Returns: 0 - */ - -static int gfs2_aspace_releasepage(struct page *page, gfp_t gfp_mask) -{ - struct inode *aspace = page->mapping->host; - struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; - struct buffer_head *bh, *head; - struct gfs2_bufdata *bd; - unsigned long t; - - if (!page_has_buffers(page)) - goto out; - - head = bh = page_buffers(page); - do { - t = jiffies; - - while (atomic_read(&bh->b_count)) { - if (atomic_read(&aspace->i_writecount)) { - if (time_after_eq(jiffies, t + - gfs2_tune_get(sdp, gt_stall_secs) * HZ)) { - stuck_releasepage(bh); - t = jiffies; - } - - yield(); - continue; - } - - return 0; - } - - gfs2_assert_warn(sdp, !buffer_pinned(bh)); - - bd = bh->b_private; - if (bd) { - gfs2_assert_warn(sdp, bd->bd_bh == bh); - gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); - gfs2_assert_warn(sdp, list_empty(&bd->bd_le.le_list)); - gfs2_assert_warn(sdp, !bd->bd_ail); - kmem_cache_free(gfs2_bufdata_cachep, bd); - bh->b_private = NULL; - } - - bh = bh->b_this_page; - } - while (bh != head); - - out: - return try_to_free_buffers(page); -} - -static struct address_space_operations aspace_aops = { +static const struct address_space_operations aspace_aops = { .writepage = gfs2_aspace_writepage, - .releasepage = gfs2_aspace_releasepage, + .releasepage = gfs2_releasepage, }; /** @@ -185,13 +71,12 @@ struct inode *gfs2_aspace_get(struct gfs2_sbd *sdp) aspace = new_inode(sdp->sd_vfs); if (aspace) { - mapping_set_gfp_mask(aspace->i_mapping, GFP_KERNEL); + mapping_set_gfp_mask(aspace->i_mapping, GFP_NOFS); aspace->i_mapping->a_ops = &aspace_aops; aspace->i_size = ~0ULL; - aspace->u.generic_ip = NULL; + aspace->i_private = NULL; insert_inode_hash(aspace); } - return aspace; } @@ -201,161 +86,6 @@ void gfs2_aspace_put(struct inode *aspace) iput(aspace); } -/** - * gfs2_ail1_start_one - Start I/O on a part of the AIL - * @sdp: the filesystem - * @tr: the part of the AIL - * - */ - -void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct gfs2_bufdata *bd, *s; - struct buffer_head *bh; - int retry; - - do { - retry = 0; - - list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, - bd_ail_st_list) { - bh = bd->bd_bh; - - gfs2_assert(sdp, bd->bd_ail == ai); - - if (!buffer_busy(bh)) { - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - list_move(&bd->bd_ail_st_list, - &ai->ai_ail2_list); - continue; - } - - if (!buffer_dirty(bh)) - continue; - - list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); - - gfs2_log_unlock(sdp); - wait_on_buffer(bh); - ll_rw_block(WRITE, 1, &bh); - gfs2_log_lock(sdp); - - retry = 1; - break; - } - } while (retry); -} - -/** - * gfs2_ail1_empty_one - Check whether or not a trans in the AIL has been synced - * @sdp: the filesystem - * @ai: the AIL entry - * - */ - -int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags) -{ - struct gfs2_bufdata *bd, *s; - struct buffer_head *bh; - - list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, - bd_ail_st_list) { - bh = bd->bd_bh; - - gfs2_assert(sdp, bd->bd_ail == ai); - - if (buffer_busy(bh)) { - if (flags & DIO_ALL) - continue; - else - break; - } - - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - - list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); - } - - return list_empty(&ai->ai_ail1_list); -} - -/** - * gfs2_ail2_empty_one - Check whether or not a trans in the AIL has been synced - * @sdp: the filesystem - * @ai: the AIL entry - * - */ - -void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct list_head *head = &ai->ai_ail2_list; - struct gfs2_bufdata *bd; - - while (!list_empty(head)) { - bd = list_entry(head->prev, struct gfs2_bufdata, - bd_ail_st_list); - gfs2_assert(sdp, bd->bd_ail == ai); - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&bd->bd_gl->gl_ail_count); - brelse(bd->bd_bh); - } -} - -/** - * ail_empty_gl - remove all buffers for a given lock from the AIL - * @gl: the glock - * - * None of the buffers should be dirty, locked, or pinned. - */ - -void gfs2_ail_empty_gl(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - unsigned int blocks; - struct list_head *head = &gl->gl_ail_list; - struct gfs2_bufdata *bd; - struct buffer_head *bh; - uint64_t blkno; - int error; - - blocks = atomic_read(&gl->gl_ail_count); - if (!blocks) - return; - - error = gfs2_trans_begin(sdp, 0, blocks); - if (gfs2_assert_withdraw(sdp, !error)) - return; - - gfs2_log_lock(sdp); - while (!list_empty(head)) { - bd = list_entry(head->next, struct gfs2_bufdata, - bd_ail_gl_list); - bh = bd->bd_bh; - blkno = bh->b_blocknr; - gfs2_assert_withdraw(sdp, !buffer_busy(bh)); - - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&gl->gl_ail_count); - brelse(bh); - gfs2_log_unlock(sdp); - - gfs2_trans_add_revoke(sdp, blkno); - - gfs2_log_lock(sdp); - } - gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); - gfs2_log_unlock(sdp); - - gfs2_trans_end(sdp); - gfs2_log_flush(sdp); -} - /** * gfs2_meta_inval - Invalidate all buffers associated with a glock * @gl: the glock @@ -380,19 +110,16 @@ void gfs2_meta_inval(struct gfs2_glock *gl) /** * gfs2_meta_sync - Sync all buffers associated with a glock * @gl: The glock - * @flags: DIO_START | DIO_WAIT * */ -void gfs2_meta_sync(struct gfs2_glock *gl, int flags) +void gfs2_meta_sync(struct gfs2_glock *gl) { struct address_space *mapping = gl->gl_aspace->i_mapping; - int error = 0; + int error; - if (flags & DIO_START) - filemap_fdatawrite(mapping); - if (!error && (flags & DIO_WAIT)) - error = filemap_fdatawait(mapping); + filemap_fdatawrite(mapping); + error = filemap_fdatawait(mapping); if (error) gfs2_io_error(gl->gl_sbd); @@ -400,17 +127,17 @@ void gfs2_meta_sync(struct gfs2_glock *gl, int flags) /** * getbuf - Get a buffer with a given address space - * @sdp: the filesystem - * @aspace: the address space + * @gl: the glock * @blkno: the block number (filesystem scope) * @create: 1 if the buffer should be created * * Returns: the buffer */ -static struct buffer_head *getbuf(struct gfs2_sbd *sdp, struct inode *aspace, - uint64_t blkno, int create) +static struct buffer_head *getbuf(struct gfs2_glock *gl, u64 blkno, int create) { + struct address_space *mapping = gl->gl_aspace->i_mapping; + struct gfs2_sbd *sdp = gl->gl_sbd; struct page *page; struct buffer_head *bh; unsigned int shift; @@ -423,13 +150,13 @@ static struct buffer_head *getbuf(struct gfs2_sbd *sdp, struct inode *aspace, if (create) { for (;;) { - page = grab_cache_page(aspace->i_mapping, index); + page = grab_cache_page(mapping, index); if (page) break; yield(); } } else { - page = find_lock_page(aspace->i_mapping, index); + page = find_lock_page(mapping, index); if (!page) return NULL; } @@ -472,10 +199,10 @@ static void meta_prep_new(struct buffer_head *bh) * Returns: The buffer */ -struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, uint64_t blkno) +struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno) { struct buffer_head *bh; - bh = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE); + bh = getbuf(gl, blkno, CREATE); meta_prep_new(bh); return bh; } @@ -484,57 +211,52 @@ struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, uint64_t blkno) * gfs2_meta_read - Read a block from disk * @gl: The glock covering the block * @blkno: The block number - * @flags: flags to gfs2_dreread() + * @flags: flags * @bhp: the place where the buffer is returned (NULL on failure) * * Returns: errno */ -int gfs2_meta_read(struct gfs2_glock *gl, uint64_t blkno, int flags, +int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, struct buffer_head **bhp) { - int error; - - *bhp = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE); - error = gfs2_meta_reread(gl->gl_sbd, *bhp, flags); - if (error) - brelse(*bhp); + *bhp = getbuf(gl, blkno, CREATE); + if (!buffer_uptodate(*bhp)) + ll_rw_block(READ_META, 1, bhp); + if (flags & DIO_WAIT) { + int error = gfs2_meta_wait(gl->gl_sbd, *bhp); + if (error) { + brelse(*bhp); + return error; + } + } - return error; + return 0; } /** - * gfs2_meta_reread - Reread a block from disk + * gfs2_meta_wait - Reread a block from disk * @sdp: the filesystem - * @bh: The block to read - * @flags: Flags that control the read + * @bh: The block to wait for * * Returns: errno */ -int gfs2_meta_reread(struct gfs2_sbd *sdp, struct buffer_head *bh, int flags) +int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh) { if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) return -EIO; - if (flags & DIO_FORCE) - clear_buffer_uptodate(bh); - - if ((flags & DIO_START) && !buffer_uptodate(bh)) - ll_rw_block(READ, 1, &bh); - - if (flags & DIO_WAIT) { - wait_on_buffer(bh); + wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - struct gfs2_trans *tr = current->journal_info; - if (tr && tr->tr_touched) - gfs2_io_error_bh(sdp, bh); - return -EIO; - } - if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - return -EIO; + if (!buffer_uptodate(bh)) { + struct gfs2_trans *tr = current->journal_info; + if (tr && tr->tr_touched) + gfs2_io_error_bh(sdp, bh); + return -EIO; } + if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) + return -EIO; return 0; } @@ -560,19 +282,15 @@ void gfs2_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh, return; } - bd = kmem_cache_alloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL), - memset(bd, 0, sizeof(struct gfs2_bufdata)); - + bd = kmem_cache_zalloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL), bd->bd_bh = bh; bd->bd_gl = gl; INIT_LIST_HEAD(&bd->bd_list_tr); - if (meta) { + if (meta) lops_init_le(&bd->bd_le, &gfs2_buf_lops); - } else { + else lops_init_le(&bd->bd_le, &gfs2_databuf_lops); - get_bh(bh); - } bh->b_private = bd; if (meta) @@ -657,14 +375,13 @@ void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, * */ -void gfs2_meta_wipe(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen) +void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen) { - struct gfs2_sbd *sdp = ip->i_sbd; - struct inode *aspace = ip->i_gl->gl_aspace; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head *bh; while (blen) { - bh = getbuf(sdp, aspace, bstart, NO_CREATE); + bh = getbuf(ip->i_gl, bstart, NO_CREATE); if (bh) { struct gfs2_bufdata *bd = bh->b_private; @@ -681,7 +398,7 @@ void gfs2_meta_wipe(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen) if (bd) { gfs2_log_lock(sdp); if (bd->bd_ail) { - uint64_t blkno = bh->b_blocknr; + u64 blkno = bh->b_blocknr; bd->bd_ail = NULL; list_del(&bd->bd_ail_st_list); list_del(&bd->bd_ail_gl_list); @@ -745,70 +462,63 @@ void gfs2_meta_cache_flush(struct gfs2_inode *ip) * Returns: errno */ -int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, uint64_t num, +int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num, int new, struct buffer_head **bhp) { - struct buffer_head *bh, **bh_slot = ip->i_cache + height; - int error; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + struct gfs2_glock *gl = ip->i_gl; + struct buffer_head *bh = NULL, **bh_slot = ip->i_cache + height; + int in_cache = 0; + + BUG_ON(!gl); + BUG_ON(!sdp); spin_lock(&ip->i_spin); - bh = *bh_slot; - if (bh) { - if (bh->b_blocknr == num) - get_bh(bh); - else - bh = NULL; + if (*bh_slot && (*bh_slot)->b_blocknr == num) { + bh = *bh_slot; + get_bh(bh); + in_cache = 1; } spin_unlock(&ip->i_spin); - if (bh) { - if (new) - meta_prep_new(bh); - else { - error = gfs2_meta_reread(ip->i_sbd, bh, - DIO_START | DIO_WAIT); - if (error) { - brelse(bh); - return error; - } - } - } else { - if (new) - bh = gfs2_meta_new(ip->i_gl, num); - else { - error = gfs2_meta_read(ip->i_gl, num, - DIO_START | DIO_WAIT, &bh); - if (error) - return error; - } + if (!bh) + bh = getbuf(gl, num, CREATE); - spin_lock(&ip->i_spin); - if (*bh_slot != bh) { - brelse(*bh_slot); - *bh_slot = bh; - get_bh(bh); - } - spin_unlock(&ip->i_spin); - } + if (!bh) + return -ENOBUFS; if (new) { - if (gfs2_assert_warn(ip->i_sbd, height)) { - brelse(bh); - return -EIO; - } + if (gfs2_assert_warn(sdp, height)) + goto err; + meta_prep_new(bh); gfs2_trans_add_bh(ip->i_gl, bh, 1); gfs2_metatype_set(bh, GFS2_METATYPE_IN, GFS2_FORMAT_IN); gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header)); + } else { + u32 mtype = height ? GFS2_METATYPE_IN : GFS2_METATYPE_DI; + if (!buffer_uptodate(bh)) { + ll_rw_block(READ_META, 1, &bh); + if (gfs2_meta_wait(sdp, bh)) + goto err; + } + if (gfs2_metatype_check(sdp, bh, mtype)) + goto err; + } - } else if (gfs2_metatype_check(ip->i_sbd, bh, - (height) ? GFS2_METATYPE_IN : GFS2_METATYPE_DI)) { - brelse(bh); - return -EIO; + if (!in_cache) { + spin_lock(&ip->i_spin); + if (*bh_slot) + brelse(*bh_slot); + *bh_slot = bh; + get_bh(bh); + spin_unlock(&ip->i_spin); } *bhp = bh; - return 0; +err: + brelse(bh); + return -EIO; } /** @@ -817,71 +527,47 @@ int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, uint64_t num, * @dblock: the starting disk block * @extlen: the number of blocks in the extent * + * returns: the first buffer in the extent */ -void gfs2_meta_ra(struct gfs2_glock *gl, uint64_t dblock, uint32_t extlen) +struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen) { struct gfs2_sbd *sdp = gl->gl_sbd; - struct inode *aspace = gl->gl_aspace; struct buffer_head *first_bh, *bh; - uint32_t max_ra = gfs2_tune_get(sdp, gt_max_readahead) >> + u32 max_ra = gfs2_tune_get(sdp, gt_max_readahead) >> sdp->sd_sb.sb_bsize_shift; - int error; - if (!extlen || !max_ra) - return; + BUG_ON(!extlen); + + if (max_ra < 1) + max_ra = 1; if (extlen > max_ra) extlen = max_ra; - first_bh = getbuf(sdp, aspace, dblock, CREATE); + first_bh = getbuf(gl, dblock, CREATE); if (buffer_uptodate(first_bh)) goto out; - if (!buffer_locked(first_bh)) { - error = gfs2_meta_reread(sdp, first_bh, DIO_START); - if (error) - goto out; - } + if (!buffer_locked(first_bh)) + ll_rw_block(READ_META, 1, &first_bh); dblock++; extlen--; while (extlen) { - bh = getbuf(sdp, aspace, dblock, CREATE); - - if (!buffer_uptodate(bh) && !buffer_locked(bh)) { - error = gfs2_meta_reread(sdp, bh, DIO_START); - brelse(bh); - if (error) - goto out; - } else - brelse(bh); + bh = getbuf(gl, dblock, CREATE); + if (!buffer_uptodate(bh) && !buffer_locked(bh)) + ll_rw_block(READA, 1, &bh); + brelse(bh); dblock++; extlen--; - - if (buffer_uptodate(first_bh)) - break; + if (!buffer_locked(first_bh) && buffer_uptodate(first_bh)) + goto out; } - out: - brelse(first_bh); -} - -/** - * gfs2_meta_syncfs - sync all the buffers in a filesystem - * @sdp: the filesystem - * - */ - -void gfs2_meta_syncfs(struct gfs2_sbd *sdp) -{ - gfs2_log_flush(sdp); - for (;;) { - gfs2_ail1_start(sdp, DIO_ALL); - if (gfs2_ail1_empty(sdp, DIO_ALL)) - break; - msleep(100); - } + wait_on_buffer(first_bh); +out: + return first_bh; }