📄 journal.c
字号:
/* buffer is in current transaction */inline int buffer_journaled(const struct buffer_head *bh) { if (bh) return test_bit(BH_JDirty, ( struct buffer_head * ) &bh->b_state) ; else return 0 ;}/* disk block was taken off free list before being in a finished transation, or written to disk** journal_new blocks can be reused immediately, for any purpose*/ inline int buffer_journal_new(const struct buffer_head *bh) { if (bh) return test_bit(BH_JNew, ( struct buffer_head * )&bh->b_state) ; else return 0 ;}inline int mark_buffer_journal_new(struct buffer_head *bh) { if (bh) { set_bit(BH_JNew, &bh->b_state) ; } return 0 ;}inline int mark_buffer_not_journaled(struct buffer_head *bh) { if (bh) clear_bit(BH_JDirty, &bh->b_state) ; return 0 ;}/* utility function to force a BUG if it is called without the big** kernel lock held. caller is the string printed just before calling BUG()*/void reiserfs_check_lock_depth(char *caller) {#ifdef CONFIG_SMP if (current->lock_depth < 0) { printk("%s called without kernel lock held\n", caller) ; show_reiserfs_locks() ; BUG() ; }#else ;#endif}/* return a cnode with same dev, block number and size in table, or null if not found */static inline struct reiserfs_journal_cnode *get_journal_hash_dev(struct reiserfs_journal_cnode **table, kdev_t dev,long bl,int size) { struct reiserfs_journal_cnode *cn ; cn = journal_hash(table, dev, bl) ; while(cn) { if ((cn->blocknr == bl) && (cn->dev == dev)) return cn ; cn = cn->hnext ; } return (struct reiserfs_journal_cnode *)0 ;}/* returns a cnode with same size, block number and dev as bh in the current transaction hash. NULL if not found */static inline struct reiserfs_journal_cnode *get_journal_hash(struct super_block *p_s_sb, struct buffer_head *bh) { struct reiserfs_journal_cnode *cn ; if (bh) { cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_hash_table, bh->b_dev, bh->b_blocknr, bh->b_size) ; } else { return (struct reiserfs_journal_cnode *)0 ; } return cn ;}/* once upon a time, the journal would deadlock. a lot. Now, when** CONFIG_REISERFS_CHECK is defined, anytime someone enters a** transaction, it pushes itself into this ugly static list, and pops** itself off before calling journal_end. I made a SysRq key to dump** the list, and tell me what the writers are when I'm deadlocked. */ /* are you depending on the compiler to optimize this function away everywhere it is called? It is not obvious how this works, but I suppose debugging code need not be clear. -Hans */static char *journal_writers[512] ;int push_journal_writer(char *s) {#ifdef CONFIG_REISERFS_CHECK int i ; for (i = 0 ; i < 512 ; i++) { if (!journal_writers[i]) { journal_writers[i] = s ; return i ; } } return -1 ;#else return 0 ;#endif}int pop_journal_writer(int index) {#ifdef CONFIG_REISERFS_CHECK if (index >= 0) { journal_writers[index] = NULL ; }#endif return 0 ;}int dump_journal_writers(void) { int i ; for (i = 0 ; i < 512 ; i++) { if (journal_writers[i]) { printk("%d: %s\n", i, journal_writers[i]) ; } } return 0 ;}/*** this actually means 'can this block be reallocated yet?'. If you set search_all, a block can only be allocated** if it is not in the current transaction, was not freed by the current transaction, and has no chance of ever** being overwritten by a replay after crashing.**** If you don't set search_all, a block can only be allocated if it is not in the current transaction. Since deleting** a block removes it from the current transaction, this case should never happen. If you don't set search_all, make** sure you never write the block without logging it.**** next_zero_bit is a suggestion about the next block to try for find_forward.** when bl is rejected because it is set in a journal list bitmap, we search** for the next zero bit in the bitmap that rejected bl. Then, we return that** through next_zero_bit for find_forward to try.**** Just because we return something in next_zero_bit does not mean we won't** reject it on the next call to reiserfs_in_journal***/int reiserfs_in_journal(struct super_block *p_s_sb, kdev_t dev, unsigned long bl, int size, int search_all, unsigned long *next_zero_bit) { struct reiserfs_journal_cnode *cn ; struct reiserfs_list_bitmap *jb ; int i ; int bmap_nr = bl / (p_s_sb->s_blocksize << 3) ; int bit_nr = bl % (p_s_sb->s_blocksize << 3) ; int tmp_bit ; *next_zero_bit = 0 ; /* always start this at zero. */ /* we aren't logging all blocks are safe for reuse */ if (reiserfs_dont_log(p_s_sb)) { return 0 ; } PROC_INFO_INC( p_s_sb, journal.in_journal ); /* If we aren't doing a search_all, this is a metablock, and it will be logged before use. ** if we crash before the transaction that freed it commits, this transaction won't ** have committed either, and the block will never be written */ if (search_all) { for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) { PROC_INFO_INC( p_s_sb, journal.in_journal_bitmap ); jb = SB_JOURNAL(p_s_sb)->j_list_bitmap + i ; if (jb->journal_list && jb->bitmaps[bmap_nr] && test_bit(bit_nr, jb->bitmaps[bmap_nr]->data)) { tmp_bit = find_next_zero_bit((unsigned long *) (jb->bitmaps[bmap_nr]->data), p_s_sb->s_blocksize << 3, bit_nr+1) ; *next_zero_bit = bmap_nr * (p_s_sb->s_blocksize << 3) + tmp_bit ; return 1 ; } } } /* is it in any old transactions? */ if (search_all && (cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_list_hash_table, dev,bl,size))) { return 1; } /* is it in the current transaction. This should never happen */ if ((cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_hash_table, dev,bl,size))) { return 1; } PROC_INFO_INC( p_s_sb, journal.in_journal_reusable ); /* safe for reuse */ return 0 ;}/* insert cn into table*/inline void insert_journal_hash(struct reiserfs_journal_cnode **table, struct reiserfs_journal_cnode *cn) { struct reiserfs_journal_cnode *cn_orig ; cn_orig = journal_hash(table, cn->dev, cn->blocknr) ; cn->hnext = cn_orig ; cn->hprev = NULL ; if (cn_orig) { cn_orig->hprev = cn ; } journal_hash(table, cn->dev, cn->blocknr) = cn ;}/* lock the current transaction */inline static void lock_journal(struct super_block *p_s_sb) { PROC_INFO_INC( p_s_sb, journal.lock_journal ); while(atomic_read(&(SB_JOURNAL(p_s_sb)->j_wlock)) > 0) { PROC_INFO_INC( p_s_sb, journal.lock_journal_wait ); sleep_on(&(SB_JOURNAL(p_s_sb)->j_wait)) ; } atomic_set(&(SB_JOURNAL(p_s_sb)->j_wlock), 1) ;}/* unlock the current transaction */inline static void unlock_journal(struct super_block *p_s_sb) { atomic_dec(&(SB_JOURNAL(p_s_sb)->j_wlock)) ; wake_up(&(SB_JOURNAL(p_s_sb)->j_wait)) ;}/*** this used to be much more involved, and I'm keeping it just in case things get ugly again.** it gets called by flush_commit_list, and cleans up any data stored about blocks freed during a** transaction.*/static void cleanup_freed_for_journal_list(struct super_block *p_s_sb, struct reiserfs_journal_list *jl) { struct reiserfs_list_bitmap *jb = jl->j_list_bitmap ; if (jb) { cleanup_bitmap_list(p_s_sb, jb) ; } jl->j_list_bitmap->journal_list = NULL ; jl->j_list_bitmap = NULL ;}/*** if this journal list still has commit blocks unflushed, send them to disk.**** log areas must be flushed in order (transaction 2 can't commit before transaction 1)** Before the commit block can by written, every other log block must be safely on disk***/static int flush_commit_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) { int i, count ; int index = 0 ; int bn ; int retry_count = 0 ; int orig_commit_left = 0 ; struct buffer_head *tbh = NULL ; struct reiserfs_journal_list *other_jl ; reiserfs_check_lock_depth("flush_commit_list") ; if (atomic_read(&jl->j_older_commits_done)) { return 0 ; } /* before we can put our commit blocks on disk, we have to make sure everyone older than ** us is on disk too */ if (jl->j_len <= 0) { return 0 ; } if (flushall) { /* we _must_ make sure the transactions are committed in order. Start with the ** index after this one, wrap all the way around */ index = (jl - SB_JOURNAL_LIST(s)) + 1 ; for (i = 0 ; i < JOURNAL_LIST_COUNT ; i++) { other_jl = SB_JOURNAL_LIST(s) + ( (index + i) % JOURNAL_LIST_COUNT) ; if (other_jl && other_jl != jl && other_jl->j_len > 0 && other_jl->j_trans_id > 0 && other_jl->j_trans_id <= jl->j_trans_id && (atomic_read(&(jl->j_older_commits_done)) == 0)) { flush_commit_list(s, other_jl, 0) ; } } } count = 0 ; /* don't flush the commit list for the current transactoin */ if (jl == ((SB_JOURNAL_LIST(s) + SB_JOURNAL_LIST_INDEX(s)))) { return 0 ; } /* make sure nobody is trying to flush this one at the same time */ if (atomic_read(&(jl->j_commit_flushing))) { sleep_on(&(jl->j_commit_wait)) ; if (flushall) { atomic_set(&(jl->j_older_commits_done), 1) ; } return 0 ; } /* this commit is done, exit */ if (atomic_read(&(jl->j_commit_left)) <= 0) { if (flushall) { atomic_set(&(jl->j_older_commits_done), 1) ; } return 0 ; } /* keeps others from flushing while we are flushing */ atomic_set(&(jl->j_commit_flushing), 1) ; if (jl->j_len > JOURNAL_TRANS_MAX) { reiserfs_panic(s, "journal-512: flush_commit_list: length is %lu, list number %d\n", jl->j_len, jl - SB_JOURNAL_LIST(s)) ; return 0 ; } orig_commit_left = atomic_read(&(jl->j_commit_left)) ; /* start by checking all the commit blocks in this transaction. ** Add anyone not on disk into tbh. Stop checking once commit_left <= 1, because that means we ** only have the commit block left */retry: count = 0 ; for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && i < (jl->j_len + 1) ; i++) { /* everything but commit_bh */ bn = reiserfs_get_journal_block(s) + (jl->j_start+i) % JOURNAL_BLOCK_COUNT; tbh = sb_get_hash_table(s, bn) ;/* kill this sanity check */if (count > (orig_commit_left + 2)) {reiserfs_panic(s, "journal-539: flush_commit_list: BAD count(%d) > orig_commit_left(%d)!\n", count, orig_commit_left) ;} if (tbh) { if (buffer_locked(tbh)) { /* wait on it, redo it just to make sure */ wait_on_buffer(tbh) ; if (!buffer_uptodate(tbh)) { reiserfs_panic(s, "journal-584, buffer write failed\n") ; } } if (buffer_dirty(tbh)) { printk("journal-569: flush_commit_list, block already dirty!\n") ; } else { mark_buffer_dirty(tbh) ; } ll_rw_block(WRITE, 1, &tbh) ; count++ ; put_bh(tbh) ; /* once for our get_hash */ } } /* wait on everyone in tbh before writing commit block*/ if (count > 0) { for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && i < (jl->j_len + 1) ; i++) { /* everything but commit_bh */ bn = reiserfs_get_journal_block(s) + (jl->j_start + i) % JOURNAL_BLOCK_COUNT ; tbh = sb_get_hash_table(s, bn) ; wait_on_buffer(tbh) ; if (!buffer_uptodate(tbh)) { reiserfs_panic(s, "journal-601, buffer write failed\n") ; } put_bh(tbh) ; /* once for our get_hash */ bforget(tbh) ; /* once due to original getblk in do_journal_end */ atomic_dec(&(jl->j_commit_left)) ; } } if (atomic_read(&(jl->j_commit_left)) != 1) { /* just the commit_bh left, flush it without calling getblk for everyone */ if (retry_count < 2) { printk("journal-582: flush_commit_list, not all log blocks on disk yet, trying again\n") ; retry_count++ ; goto retry; } reiserfs_panic(s, "journal-563: flush_commit_list: BAD, j_commit_left is %u, should be 1\n", atomic_read(&(jl->j_commit_left))); } mark_buffer_dirty(jl->j_commit_bh) ; ll_rw_block(WRITE, 1, &(jl->j_commit_bh)) ; wait_on_buffer(jl->j_commit_bh) ; if (!buffer_uptodate(jl->j_commit_bh)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -