📄 journal.c
字号:
/* if someone is getting the commit list, we must wait for them */ while (atomic_read(&(jl->j_commit_flushing))) { sleep_on(&(jl->j_commit_wait)) ; } /* if someone is flushing this list, we must wait for them */ while (atomic_read(&(jl->j_flushing))) { sleep_on(&(jl->j_flush_wait)) ; } /* was it flushed while we slept? */ if (jl->j_len <= 0 || jl->j_trans_id != orig_trans_id) { return 0 ; } /* this list is now ours, we can change anything we want */ atomic_set(&(jl->j_flushing), 1) ;loop_start: cn = jl->j_realblock ; while(cn) { saved_bh = NULL ; /* if the blocknr == 0, this has been cleared from the hash, ** skip it */ if (cn->blocknr == 0) { goto next ; } /* look for a more recent transaction that logged this ** buffer. Only the most recent transaction with a buffer in ** it is allowed to send that buffer to disk */ pjl = find_newer_jl_for_cn(cn) ; if (run == 0 && !pjl && cn->bh && buffer_journal_dirty(cn->bh) && can_dirty(cn)) { if (!test_bit(BH_JPrepared, &cn->bh->b_state)) { set_bit(BLOCK_NEEDS_FLUSH, &cn->state) ; submit_logged_buffer(cn->bh) ; } else { /* someone else is using this buffer. We can't ** send it to disk right now because they might ** be changing/logging it. */ ret = 1 ; } } else if (test_bit(BLOCK_NEEDS_FLUSH, &cn->state)) { clear_bit(BLOCK_NEEDS_FLUSH, &cn->state) ; if (!pjl && cn->bh) { wait_on_buffer(cn->bh) ; } /* check again, someone could have logged while we scheduled */ pjl = find_newer_jl_for_cn(cn) ; /* before the JDirty_wait bit is set, the ** buffer is added to the hash list. So, if we are ** run in the middle of a do_journal_end, we will notice ** if this buffer was logged and added from the latest ** transaction. In this case, we don't want to decrement ** b_count */ if (!pjl && cn->bh && buffer_journal_dirty(cn->bh)) { blocknr = cn->blocknr ; walk_cn = cn ; saved_bh= cn->bh ; /* update all older transactions to show this block ** was flushed */ mark_buffer_notjournal_dirty(cn->bh) ; while(walk_cn) { if (walk_cn->bh && walk_cn->blocknr == blocknr && walk_cn->dev == cn->dev) { if (walk_cn->jlist) { atomic_dec(&(walk_cn->jlist->j_nonzerolen)) ; } walk_cn->bh = NULL ; } walk_cn = walk_cn->hnext ; } if (atomic_read(&saved_bh->b_count) < 1) { reiserfs_warning("clm-2081: bad count on %lu\n", saved_bh->b_blocknr) ; } brelse(saved_bh) ; } } /* ** if the more recent transaction is committed to the log, ** this buffer can be considered flushed. Decrement our ** counters to reflect one less buffer that needs writing. ** ** note, this relies on all of the above code being ** schedule free once pjl comes back non-null. */ if (pjl && cn->bh && atomic_read(&pjl->j_commit_left) == 0) { atomic_dec(&cn->jlist->j_nonzerolen) ; cn->bh = NULL ; } next: cn = cn->next ; } /* the first run through the loop sends all the dirty buffers to ** ll_rw_block. ** the second run through the loop does all the accounting */ if (run++ == 0) { goto loop_start ; } atomic_set(&(jl->j_flushing), 0) ; wake_up(&(jl->j_flush_wait)) ; return ret ;}/* since we never give dirty buffers to bdflush/kupdate, we have to** flush them ourselves. This runs through the journal lists, finds** old metadata in need of flushing and sends it to disk.** this does not end transactions, commit anything, or free** cnodes.**** returns the highest transaction id that was flushed last time*/static unsigned long reiserfs_journal_kupdate(struct super_block *s) { struct reiserfs_journal_list *jl ; int i ; int start ; time_t age ; int ret = 0 ; start = SB_JOURNAL_LIST_INDEX(s) ; /* safety check to prevent flush attempts during a mount */ if (start < 0) { return 0 ; } i = (start + 1) % JOURNAL_LIST_COUNT ; while(i != start) { jl = SB_JOURNAL_LIST(s) + i ; age = CURRENT_TIME - jl->j_timestamp ; if (jl->j_len > 0 && // age >= (JOURNAL_MAX_COMMIT_AGE * 2) && atomic_read(&(jl->j_nonzerolen)) > 0 && atomic_read(&(jl->j_commit_left)) == 0) { if (jl->j_trans_id == SB_JOURNAL(s)->j_trans_id) { break ; } /* if ret was already 1, we want to preserve that */ ret |= kupdate_one_transaction(s, jl) ; } if (atomic_read(&(jl->j_nonzerolen)) > 0) { ret |= 1 ; } i = (i + 1) % JOURNAL_LIST_COUNT ; } return ret ;}/*** removes any nodes in table with name block and dev as bh.** only touchs the hnext and hprev pointers.*/void remove_journal_hash(struct reiserfs_journal_cnode **table, struct reiserfs_journal_list *jl,struct buffer_head *bh, int remove_freed){ struct reiserfs_journal_cnode *cur ; struct reiserfs_journal_cnode **head ; if (!bh) return ; head= &(journal_hash(table, bh->b_dev, bh->b_blocknr)) ; if (!head) { return ; } cur = *head ; while(cur) { if (cur->blocknr == bh->b_blocknr && cur->dev == bh->b_dev && (jl == NULL || jl == cur->jlist) && (!test_bit(BLOCK_FREED, &cur->state) || remove_freed)) { if (cur->hnext) { cur->hnext->hprev = cur->hprev ; } if (cur->hprev) { cur->hprev->hnext = cur->hnext ; } else { *head = cur->hnext ; } cur->blocknr = 0 ; cur->dev = 0 ; cur->state = 0 ; if (cur->bh && cur->jlist) /* anybody who clears the cur->bh will also dec the nonzerolen */ atomic_dec(&(cur->jlist->j_nonzerolen)) ; cur->bh = NULL ; cur->jlist = NULL ; } cur = cur->hnext ; }}static void free_journal_ram(struct super_block *p_s_sb) { vfree(SB_JOURNAL(p_s_sb)->j_cnode_free_orig) ; free_list_bitmaps(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap) ; free_bitmap_nodes(p_s_sb) ; /* must be after free_list_bitmaps */ if (SB_JOURNAL(p_s_sb)->j_header_bh) { brelse(SB_JOURNAL(p_s_sb)->j_header_bh) ; } vfree(SB_JOURNAL(p_s_sb)) ;}/*** call on unmount. Only set error to 1 if you haven't made your way out** of read_super() yet. Any other caller must keep error at 0.*/static int do_journal_release(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, int error) { struct reiserfs_transaction_handle myth ; /* we only want to flush out transactions if we were called with error == 0 */ if (!error && !(p_s_sb->s_flags & MS_RDONLY)) { /* end the current trans */ do_journal_end(th, p_s_sb,10, FLUSH_ALL) ; /* make sure something gets logged to force our way into the flush code */ journal_join(&myth, p_s_sb, 1) ; reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; journal_mark_dirty(&myth, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; do_journal_end(&myth, p_s_sb,1, FLUSH_ALL) ; } /* we decrement before we wake up, because the commit thread dies off ** when it has been woken up and the count is <= 0 */ reiserfs_mounted_fs_count-- ; wake_up(&reiserfs_commit_thread_wait) ; sleep_on(&reiserfs_commit_thread_done) ; free_journal_ram(p_s_sb) ; return 0 ;}/*** call on unmount. flush all journal trans, release all alloc'd ram*/int journal_release(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb) { return do_journal_release(th, p_s_sb, 0) ;}/*** only call from an error condition inside reiserfs_read_super!*/int journal_release_error(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb) { return do_journal_release(th, p_s_sb, 1) ;}/* compares description block with commit block. returns 1 if they differ, 0 if they are the same */static int journal_compare_desc_commit(struct super_block *p_s_sb, struct reiserfs_journal_desc *desc, struct reiserfs_journal_commit *commit) { if (le32_to_cpu(commit->j_trans_id) != le32_to_cpu(desc->j_trans_id) || le32_to_cpu(commit->j_len) != le32_to_cpu(desc->j_len) || le32_to_cpu(commit->j_len) > JOURNAL_TRANS_MAX || le32_to_cpu(commit->j_len) <= 0 ) { return 1 ; } return 0 ;}/* returns 0 if it did not find a description block ** returns -1 if it found a corrupt commit block** returns 1 if both desc and commit were valid */static int journal_transaction_is_valid(struct super_block *p_s_sb, struct buffer_head *d_bh, unsigned long *oldest_invalid_trans_id, unsigned long *newest_mount_id) { struct reiserfs_journal_desc *desc ; struct reiserfs_journal_commit *commit ; struct buffer_head *c_bh ; unsigned long offset ; if (!d_bh) return 0 ; desc = (struct reiserfs_journal_desc *)d_bh->b_data ; if (le32_to_cpu(desc->j_len) > 0 && !memcmp(desc->j_magic, JOURNAL_DESC_MAGIC, 8)) { if (oldest_invalid_trans_id && *oldest_invalid_trans_id && le32_to_cpu(desc->j_trans_id) > *oldest_invalid_trans_id) { reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-986: transaction " "is valid returning because trans_id %d is greater than " "oldest_invalid %lu\n", le32_to_cpu(desc->j_trans_id), *oldest_invalid_trans_id); return 0 ; } if (newest_mount_id && *newest_mount_id > le32_to_cpu(desc->j_mount_id)) { reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1087: transaction " "is valid returning because mount_id %d is less than " "newest_mount_id %lu\n", desc->j_mount_id, *newest_mount_id) ; return -1 ; } offset = d_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb) ; /* ok, we have a journal description block, lets see if the transaction was valid */ c_bh = sb_bread(p_s_sb, reiserfs_get_journal_block(p_s_sb) + ((offset + le32_to_cpu(desc->j_len) + 1) % JOURNAL_BLOCK_COUNT)) ; if (!c_bh) return 0 ; commit = (struct reiserfs_journal_commit *)c_bh->b_data ; if (journal_compare_desc_commit(p_s_sb, desc, commit)) { reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal_transaction_is_valid, commit offset %ld had bad " "time %d or length %d\n", c_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb), le32_to_cpu(commit->j_trans_id), le32_to_cpu(commit->j_len)); brelse(c_bh) ; if (oldest_invalid_trans_id) *oldest_invalid_trans_id = le32_to_cpu(desc->j_trans_id) ; reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1004: " "transaction_is_valid setting oldest invalid trans_id " "to %d\n", le32_to_cpu(desc->j_trans_id)) ; return -1; } brelse(c_bh) ; reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1006: found valid " "transaction start offset %lu, len %d id %d\n", d_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb), le32_to_cpu(desc->j_len), le32_to_cpu(desc->j_trans_id)) ; return 1 ; } else { return 0 ; }}static void brelse_array(struct buffer_head **heads, int num) { int i ; for (i = 0 ; i < num ; i++) { brelse(heads[i]) ; }}/*** given the start, and values for the oldest acceptable transactions,** this either reads in a replays a transaction, or returns because the transaction** is invalid, or too old.*/static int journal_read_transaction(struct super_block *p_s_sb, unsigned long cur_dblock, unsigned long oldest_start, unsigned long oldest_trans_id, unsigned long newest_mount_id) { struct reiserfs_journal_desc *desc ; struct reiserfs_journal_commit *commit ; unsigned long trans_id = 0 ; struct buffer_head *c_bh ; struct buffer_head *d_bh ; struct buffer_head **log_blocks = NULL ; struct buffer_head **real_blocks = NULL ; unsigned long trans_offset ; int i; d_bh = sb_bread(p_s_sb, cur_dblock) ; if (!d_bh) return 1 ; desc = (struct reiserfs_journal_desc *)d_bh->b_data ; trans_offset = d_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb) ; reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1037: " "journal_read_transaction, offset %lu, len %d mount_id %d\n", d_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb), le32_to_cpu(desc->j_len), le32_to_cpu(desc->j_mount_id)) ; if (le32_to_cpu(desc->j_trans_id) < oldest_trans_id) { reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1039: " "journal_read_trans skipping because %lu is too old\n", cur_dblock - reiserfs_get_journal_block(p_s_sb)) ; brelse(d_bh) ; return 1 ; } if (le32_to_cpu(desc->j_mount_id) != newest_mount_id) { reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1146: " "journal_read_trans skipping because %d is != " "newest_mount_id %lu\n", le32_to_cpu(desc->j_mount_id), newest_mount_id) ; brelse(d_bh) ; return 1 ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -