X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fgfs2%2Flog.c;h=291415ddfe51cb291ccd66b23703e63a58b089d7;hb=ecd27b92fbb41f779d857632a69bd45dbaf0f915;hp=0b26d6a74118fbbd818133d5e4bc3e6b0de5aa11;hpb=ed3865079b573ef55dc13ab0bfb242ed5ebab4c1;p=linux-2.6 diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 0b26d6a741..291415ddfe 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.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 @@ -14,10 +14,10 @@ #include #include #include -#include +#include +#include #include "gfs2.h" -#include "lm_interface.h" #include "incore.h" #include "bmap.h" #include "glock.h" @@ -48,8 +48,7 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, unsigned int first, second; blks = 1; - first = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / - ssize; + first = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / ssize; if (nstruct > first) { second = (sdp->sd_sb.sb_bsize - @@ -60,12 +59,97 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, return blks; } -void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) +/** + * gfs2_ail1_start_one - Start I/O on a part of the AIL + * @sdp: the filesystem + * @tr: the part of the AIL + * + */ + +static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) +{ + struct gfs2_bufdata *bd, *s; + struct buffer_head *bh; + int retry; + + BUG_ON(!spin_is_locked(&sdp->sd_log_lock)); + + 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_log_unlock(sdp); + gfs2_io_error_bh(sdp, bh); + gfs2_log_lock(sdp); + } + 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 + * + */ + +static 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); +} + +static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) { struct list_head *head = &sdp->sd_ail1_list; - uint64_t sync_gen; - struct list_head *first, *tmp; - struct gfs2_ail *first_ai, *ai; + u64 sync_gen; + struct list_head *first; + struct gfs2_ail *first_ai, *ai, *tmp; + int done = 0; gfs2_log_lock(sdp); if (list_empty(head)) { @@ -77,27 +161,25 @@ void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) first = head->prev; first_ai = list_entry(first, struct gfs2_ail, ai_list); first_ai->ai_sync_gen = sync_gen; - gfs2_ail1_start_one(sdp, first_ai); + gfs2_ail1_start_one(sdp, first_ai); /* This may drop log lock */ if (flags & DIO_ALL) first = NULL; - for (;;) { + while(!done) { if (first && (head->prev != first || gfs2_ail1_empty_one(sdp, first_ai, 0))) break; - for (tmp = head->prev; tmp != head; tmp = tmp->prev) { - ai = list_entry(tmp, struct gfs2_ail, ai_list); + done = 1; + list_for_each_entry_safe_reverse(ai, tmp, head, ai_list) { if (ai->ai_sync_gen >= sync_gen) continue; ai->ai_sync_gen = sync_gen; - gfs2_ail1_start_one(sdp, ai); + gfs2_ail1_start_one(sdp, ai); /* This may drop log lock */ + done = 0; break; } - - if (tmp == head) - break; } gfs2_log_unlock(sdp); @@ -124,6 +206,31 @@ int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags) return ret; } + +/** + * gfs2_ail2_empty_one - Check whether or not a trans in the AIL has been synced + * @sdp: the filesystem + * @ai: the AIL entry + * + */ + +static 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); + } +} + static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) { struct gfs2_ail *ai, *safe; @@ -155,6 +262,12 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) * @sdp: The GFS2 superblock * @blks: The number of blocks to reserve * + * Note that we never give out the last 6 blocks of the journal. Thats + * due to the fact that there is are a small number of header blocks + * associated with each log flush. The exact number can't be known until + * flush time, so we ensure that we have just enough free blocks at all + * times to avoid running out during a log flush. + * * Returns: errno */ @@ -168,7 +281,7 @@ int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks) mutex_lock(&sdp->sd_log_reserve_mutex); gfs2_log_lock(sdp); - while(sdp->sd_log_blks_free <= blks) { + while(sdp->sd_log_blks_free <= (blks + 6)) { gfs2_log_unlock(sdp); gfs2_ail1_empty(sdp, 0); gfs2_log_flush(sdp, NULL); @@ -178,7 +291,6 @@ int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks) gfs2_log_lock(sdp); } sdp->sd_log_blks_free -= blks; - /* printk(KERN_INFO "reserved %u blocks (%u left)\n", blks, sdp->sd_log_blks_free); */ gfs2_log_unlock(sdp); mutex_unlock(&sdp->sd_log_reserve_mutex); @@ -199,24 +311,26 @@ void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) gfs2_log_lock(sdp); sdp->sd_log_blks_free += blks; - /* printk(KERN_INFO "released %u blocks (%u left)\n", blks, sdp->sd_log_blks_free); */ gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); gfs2_log_unlock(sdp); up_read(&sdp->sd_log_flush_lock); } -static uint64_t log_bmap(struct gfs2_sbd *sdp, unsigned int lbn) +static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn) { - int new = 0; - uint64_t dbn; + struct inode *inode = sdp->sd_jdesc->jd_inode; int error; + struct buffer_head bh_map = { .b_state = 0, .b_blocknr = 0 }; - error = gfs2_block_map(sdp->sd_jdesc->jd_inode->u.generic_ip, - lbn, &new, &dbn, NULL); - gfs2_assert_withdraw(sdp, !error && dbn); + bh_map.b_size = 1 << inode->i_blkbits; + error = gfs2_block_map(inode, lbn, 0, &bh_map); + if (error || !bh_map.b_blocknr) + printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error, + (unsigned long long)bh_map.b_blocknr, lbn); + gfs2_assert_withdraw(sdp, !error && bh_map.b_blocknr); - return dbn; + return bh_map.b_blocknr; } /** @@ -231,8 +345,7 @@ static uint64_t log_bmap(struct gfs2_sbd *sdp, unsigned int lbn) * Returns: the distance in blocks */ -static inline unsigned int log_distance(struct gfs2_sbd *sdp, - unsigned int newer, +static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer, unsigned int older) { int dist; @@ -251,11 +364,10 @@ static unsigned int current_tail(struct gfs2_sbd *sdp) gfs2_log_lock(sdp); - if (list_empty(&sdp->sd_ail1_list)) + if (list_empty(&sdp->sd_ail1_list)) { tail = sdp->sd_log_head; - else { - ai = list_entry(sdp->sd_ail1_list.prev, - struct gfs2_ail, ai_list); + } else { + ai = list_entry(sdp->sd_ail1_list.prev, struct gfs2_ail, ai_list); tail = ai->ai_first; } @@ -267,8 +379,7 @@ static unsigned int current_tail(struct gfs2_sbd *sdp) static inline void log_incr_head(struct gfs2_sbd *sdp) { if (sdp->sd_log_flush_head == sdp->sd_log_tail) - gfs2_assert_withdraw(sdp, - sdp->sd_log_flush_head == sdp->sd_log_head); + gfs2_assert_withdraw(sdp, sdp->sd_log_flush_head == sdp->sd_log_head); if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) { sdp->sd_log_flush_head = 0; @@ -285,7 +396,7 @@ static inline void log_incr_head(struct gfs2_sbd *sdp) struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp) { - uint64_t blkno = log_bmap(sdp, sdp->sd_log_flush_head); + u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); struct gfs2_log_buf *lb; struct buffer_head *bh; @@ -315,7 +426,7 @@ struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp) struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, struct buffer_head *real) { - uint64_t blkno = log_bmap(sdp, sdp->sd_log_flush_head); + u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); struct gfs2_log_buf *lb; struct buffer_head *bh; @@ -343,10 +454,8 @@ static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull) ail2_empty(sdp, new_tail); gfs2_log_lock(sdp); - sdp->sd_log_blks_free += dist - ((pull) ? 1 : 0); - /* printk(KERN_INFO "pull tail refunding %u blocks (%u left) pull=%d\n", dist - ((pull) ? 1 : 0), sdp->sd_log_blks_free, pull); */ - gfs2_assert_withdraw(sdp, - sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); + sdp->sd_log_blks_free += dist - (pull ? 1 : 0); + gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); gfs2_log_unlock(sdp); sdp->sd_log_tail = new_tail; @@ -359,15 +468,13 @@ static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull) * Returns: the initialized log buffer descriptor */ -static void log_write_header(struct gfs2_sbd *sdp, uint32_t flags, int pull) +static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) { - uint64_t blkno = log_bmap(sdp, sdp->sd_log_flush_head); + u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); struct buffer_head *bh; struct gfs2_log_header *lh; unsigned int tail; - uint32_t hash; - - /* printk(KERN_INFO "log write header start (flags=%08x, pull=%d)\n", flags, pull); */ + u32 hash; bh = sb_getblk(sdp->sd_vfs, blkno); lock_buffer(bh); @@ -384,10 +491,10 @@ static void log_write_header(struct gfs2_sbd *sdp, uint32_t flags, int pull) lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC); lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH); lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH); - lh->lh_sequence = be64_to_cpu(sdp->sd_log_sequence++); - lh->lh_flags = be32_to_cpu(flags); - lh->lh_tail = be32_to_cpu(tail); - lh->lh_blkno = be32_to_cpu(sdp->sd_log_flush_head); + lh->lh_sequence = cpu_to_be64(sdp->sd_log_sequence++); + lh->lh_flags = cpu_to_be32(flags); + lh->lh_tail = cpu_to_be32(tail); + lh->lh_blkno = cpu_to_be32(sdp->sd_log_flush_head); hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header)); lh->lh_hash = cpu_to_be32(hash); @@ -403,8 +510,6 @@ static void log_write_header(struct gfs2_sbd *sdp, uint32_t flags, int pull) sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); log_incr_head(sdp); - - /* printk(KERN_INFO "log write header out\n"); */ } static void log_flush_commit(struct gfs2_sbd *sdp) @@ -412,11 +517,6 @@ static void log_flush_commit(struct gfs2_sbd *sdp) struct list_head *head = &sdp->sd_log_flush_list; struct gfs2_log_buf *lb; struct buffer_head *bh; - unsigned int d; - - d = log_distance(sdp, sdp->sd_log_flush_head, sdp->sd_log_head); - - gfs2_assert_withdraw(sdp, d + 1 == sdp->sd_log_blks_reserved); while (!list_empty(head)) { lb = list_entry(head->next, struct gfs2_log_buf, lb_list); @@ -465,8 +565,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) INIT_LIST_HEAD(&ai->ai_ail1_list); INIT_LIST_HEAD(&ai->ai_ail2_list); - gfs2_assert_withdraw(sdp, - sdp->sd_log_num_buf == sdp->sd_log_commited_buf); + gfs2_assert_withdraw(sdp, sdp->sd_log_num_buf == sdp->sd_log_commited_buf); gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke); @@ -480,16 +579,15 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle) log_write_header(sdp, 0, PULL); lops_after_commit(sdp, ai); - sdp->sd_log_head = sdp->sd_log_flush_head; - - /* printk(KERN_INFO "sd_log_num_hdrs %u\n", sdp->sd_log_num_hdrs); */ - - sdp->sd_log_blks_reserved = - sdp->sd_log_commited_buf = - sdp->sd_log_num_hdrs = - sdp->sd_log_commited_revoke = 0; gfs2_log_lock(sdp); + sdp->sd_log_head = sdp->sd_log_flush_head; + sdp->sd_log_blks_free -= sdp->sd_log_num_hdrs; + sdp->sd_log_blks_reserved = 0; + sdp->sd_log_commited_buf = 0; + sdp->sd_log_num_hdrs = 0; + sdp->sd_log_commited_revoke = 0; + if (!list_empty(&ai->ai_ail1_list)) { list_add(&ai->ai_list, &sdp->sd_ail1_list); ai = NULL; @@ -504,7 +602,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { - unsigned int reserved = 1; + unsigned int reserved = 0; unsigned int old; gfs2_log_lock(sdp); @@ -515,11 +613,12 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0); if (sdp->sd_log_commited_buf) - reserved += 1 + sdp->sd_log_commited_buf + - sdp->sd_log_commited_buf/503; + reserved += sdp->sd_log_commited_buf; if (sdp->sd_log_commited_revoke) reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke, - sizeof(uint64_t)); + sizeof(u64)); + if (reserved) + reserved++; old = sdp->sd_log_blks_free; sdp->sd_log_blks_free += tr->tr_reserved - @@ -527,7 +626,8 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free >= old); gfs2_assert_withdraw(sdp, - sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); + sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks + + sdp->sd_log_num_hdrs); sdp->sd_log_blks_reserved = reserved; @@ -551,11 +651,9 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) up_read(&sdp->sd_log_flush_lock); gfs2_log_lock(sdp); - if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) { - gfs2_log_unlock(sdp); - gfs2_log_flush(sdp, NULL); - } else - gfs2_log_unlock(sdp); + if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) + wake_up_process(sdp->sd_logd_process); + gfs2_log_unlock(sdp); } /** @@ -575,6 +673,7 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf); + gfs2_assert_withdraw(sdp, !sdp->sd_log_num_hdrs); gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list)); sdp->sd_log_flush_head = sdp->sd_log_head; @@ -582,10 +681,9 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0); - gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free == - sdp->sd_jdesc->jd_blocks); - gfs2_assert_withdraw(sdp, sdp->sd_log_head == sdp->sd_log_tail); - gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail2_list)); + gfs2_assert_warn(sdp, sdp->sd_log_blks_free == sdp->sd_jdesc->jd_blocks); + gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail); + gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list)); sdp->sd_log_head = sdp->sd_log_flush_head; sdp->sd_log_tail = sdp->sd_log_head; @@ -593,3 +691,21 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) up_write(&sdp->sd_log_flush_lock); } + +/** + * 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, NULL); + for (;;) { + gfs2_ail1_start(sdp, DIO_ALL); + if (gfs2_ail1_empty(sdp, DIO_ALL)) + break; + msleep(10); + } +} +