📄 journal.c
字号:
/*** Write ahead logging implementation copyright Chris Mason 2000**** The background commits make this code very interelated, and ** overly complex. I need to rethink things a bit....The major players:**** journal_begin -- call with the number of blocks you expect to log. ** If the current transaction is too** old, it will block until the current transaction is ** finished, and then start a new one.** Usually, your transaction will get joined in with ** previous ones for speed.**** journal_join -- same as journal_begin, but won't block on the current ** transaction regardless of age. Don't ever call** this. Ever. There are only two places it should be ** called from, and they are both inside this file.**** journal_mark_dirty -- adds blocks into this transaction. clears any flags ** that might make them get sent to disk** and then marks them BH_JDirty. Puts the buffer head ** into the current transaction hash. **** journal_end -- if the current transaction is batchable, it does nothing** otherwise, it could do an async/synchronous commit, or** a full flush of all log and real blocks in the ** transaction.**** flush_old_commits -- if the current transaction is too old, it is ended and ** commit blocks are sent to disk. Forces commit blocks ** to disk for all backgrounded commits that have been ** around too long.** -- Note, if you call this as an immediate flush from ** from within kupdate, it will ignore the immediate flag**** The commit thread -- a writer process for async commits. It allows a ** a process to request a log flush on a task queue.** the commit will happen once the commit thread wakes up.** The benefit here is the writer (with whatever** related locks it has) doesn't have to wait for the** log blocks to hit disk if it doesn't want to.*/#include <linux/config.h>#include <asm/uaccess.h>#include <asm/system.h>#include <linux/sched.h>#include <asm/semaphore.h>#include <linux/vmalloc.h>#include <linux/reiserfs_fs.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/fcntl.h>#include <linux/locks.h>#include <linux/stat.h>#include <linux/string.h>#include <linux/smp_lock.h>/* the number of mounted filesystems. This is used to decide when to** start and kill the commit thread*/static int reiserfs_mounted_fs_count = 0 ;/* wake this up when you add something to the commit thread task queue */DECLARE_WAIT_QUEUE_HEAD(reiserfs_commit_thread_wait) ;/* wait on this if you need to be sure you task queue entries have been run */static DECLARE_WAIT_QUEUE_HEAD(reiserfs_commit_thread_done) ;DECLARE_TASK_QUEUE(reiserfs_commit_thread_tq) ;#define JOURNAL_TRANS_HALF 1018 /* must be correct to keep the desc and commit structs at 4k *//* cnode stat bits. Move these into reiserfs_fs.h */#define BLOCK_FREED 2 /* this block was freed, and can't be written. */#define BLOCK_FREED_HOLDER 3 /* this block was freed during this transaction, and can't be written */#define BLOCK_NEEDS_FLUSH 4 /* used in flush_journal_list *//* flags for do_journal_end */#define FLUSH_ALL 1 /* flush commit and real blocks */#define COMMIT_NOW 2 /* end and commit this transaction */#define WAIT 4 /* wait for the log blocks to hit the disk*//* state bits for the journal */#define WRITERS_BLOCKED 1 /* set when new writers not allowed */static int do_journal_end(struct reiserfs_transaction_handle *,struct super_block *,unsigned long nblocks,int flags) ;static int flush_journal_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) ;static int flush_commit_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) ;static int can_dirty(struct reiserfs_journal_cnode *cn) ;static int remove_from_journal_list(struct super_block *s, struct reiserfs_journal_list *jl, struct buffer_head *bh, int remove_freed);static int journal_join(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks);static void init_journal_hash(struct super_block *p_s_sb) { memset(SB_JOURNAL(p_s_sb)->j_hash_table, 0, JOURNAL_HASH_SIZE * sizeof(struct reiserfs_journal_cnode *)) ;}/*** clears BH_Dirty and sticks the buffer on the clean list. Called because I can't allow refile_buffer to** make schedule happen after I've freed a block. Look at remove_from_transaction and journal_mark_freed for** more details.*/static int reiserfs_clean_and_file_buffer(struct buffer_head *bh) { if (bh) { clear_bit(BH_Dirty, &bh->b_state) ; refile_buffer(bh) ; } return 0 ;}static struct reiserfs_bitmap_node *allocate_bitmap_node(struct super_block *p_s_sb) { struct reiserfs_bitmap_node *bn ; static int id = 0 ; bn = kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS) ; if (!bn) { return NULL ; } bn->data = kmalloc(p_s_sb->s_blocksize, GFP_NOFS) ; if (!bn->data) { kfree(bn) ; return NULL ; } bn->id = id++ ; memset(bn->data, 0, p_s_sb->s_blocksize) ; INIT_LIST_HEAD(&bn->list) ; return bn ;}static struct reiserfs_bitmap_node *get_bitmap_node(struct super_block *p_s_sb) { struct reiserfs_bitmap_node *bn = NULL; struct list_head *entry = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ; SB_JOURNAL(p_s_sb)->j_used_bitmap_nodes++ ;repeat: if(entry != &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) { bn = list_entry(entry, struct reiserfs_bitmap_node, list) ; list_del(entry) ; memset(bn->data, 0, p_s_sb->s_blocksize) ; SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes-- ; return bn ; } bn = allocate_bitmap_node(p_s_sb) ; if (!bn) { current->policy |= SCHED_YIELD ; schedule() ; goto repeat ; } return bn ;}static inline void free_bitmap_node(struct super_block *p_s_sb, struct reiserfs_bitmap_node *bn) { SB_JOURNAL(p_s_sb)->j_used_bitmap_nodes-- ; if (SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes > REISERFS_MAX_BITMAP_NODES) { kfree(bn->data) ; kfree(bn) ; } else { list_add(&bn->list, &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ; SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes++ ; }}static void allocate_bitmap_nodes(struct super_block *p_s_sb) { int i ; struct reiserfs_bitmap_node *bn = NULL ; for (i = 0 ; i < REISERFS_MIN_BITMAP_NODES ; i++) { bn = allocate_bitmap_node(p_s_sb) ; if (bn) { list_add(&bn->list, &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ; SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes++ ; } else { break ; // this is ok, we'll try again when more are needed } }}static int set_bit_in_list_bitmap(struct super_block *p_s_sb, int block, struct reiserfs_list_bitmap *jb) { int bmap_nr = block / (p_s_sb->s_blocksize << 3) ; int bit_nr = block % (p_s_sb->s_blocksize << 3) ; if (!jb->bitmaps[bmap_nr]) { jb->bitmaps[bmap_nr] = get_bitmap_node(p_s_sb) ; } set_bit(bit_nr, jb->bitmaps[bmap_nr]->data) ; return 0 ;}static void cleanup_bitmap_list(struct super_block *p_s_sb, struct reiserfs_list_bitmap *jb) { int i; for (i = 0 ; i < SB_BMAP_NR(p_s_sb) ; i++) { if (jb->bitmaps[i]) { free_bitmap_node(p_s_sb, jb->bitmaps[i]) ; jb->bitmaps[i] = NULL ; } }}/*** only call this on FS unmount.*/static int free_list_bitmaps(struct super_block *p_s_sb, struct reiserfs_list_bitmap *jb_array) { int i ; struct reiserfs_list_bitmap *jb ; for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) { jb = jb_array + i ; jb->journal_list = NULL ; cleanup_bitmap_list(p_s_sb, jb) ; vfree(jb->bitmaps) ; jb->bitmaps = NULL ; } return 0;}static int free_bitmap_nodes(struct super_block *p_s_sb) { struct list_head *next = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ; struct reiserfs_bitmap_node *bn ; while(next != &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) { bn = list_entry(next, struct reiserfs_bitmap_node, list) ; list_del(next) ; kfree(bn->data) ; kfree(bn) ; next = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ; SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes-- ; } return 0 ;}/*** get memory for JOURNAL_NUM_BITMAPS worth of bitmaps. ** jb_array is the array to be filled in.*/int reiserfs_allocate_list_bitmaps(struct super_block *p_s_sb, struct reiserfs_list_bitmap *jb_array, int bmap_nr) { int i ; int failed = 0 ; struct reiserfs_list_bitmap *jb ; int mem = bmap_nr * sizeof(struct reiserfs_bitmap_node *) ; for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) { jb = jb_array + i ; jb->journal_list = NULL ; jb->bitmaps = vmalloc( mem ) ; if (!jb->bitmaps) { reiserfs_warning("clm-2000, unable to allocate bitmaps for journal lists\n") ; failed = 1; break ; } memset(jb->bitmaps, 0, mem) ; } if (failed) { free_list_bitmaps(p_s_sb, jb_array) ; return -1 ; } return 0 ;}/*** find an available list bitmap. If you can't find one, flush a commit list ** and try again*/static struct reiserfs_list_bitmap *get_list_bitmap(struct super_block *p_s_sb, struct reiserfs_journal_list *jl) { int i,j ; struct reiserfs_list_bitmap *jb = NULL ; for (j = 0 ; j < (JOURNAL_NUM_BITMAPS * 3) ; j++) { i = SB_JOURNAL(p_s_sb)->j_list_bitmap_index ; SB_JOURNAL(p_s_sb)->j_list_bitmap_index = (i + 1) % JOURNAL_NUM_BITMAPS ; jb = SB_JOURNAL(p_s_sb)->j_list_bitmap + i ; if (SB_JOURNAL(p_s_sb)->j_list_bitmap[i].journal_list) { flush_commit_list(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap[i].journal_list, 1) ; if (!SB_JOURNAL(p_s_sb)->j_list_bitmap[i].journal_list) { break ; } } else { break ; } } if (jb->journal_list) { /* double check to make sure if flushed correctly */ return NULL ; } jb->journal_list = jl ; return jb ;}/* ** allocates a new chunk of X nodes, and links them all together as a list.** Uses the cnode->next and cnode->prev pointers** returns NULL on failure*/static struct reiserfs_journal_cnode *allocate_cnodes(int num_cnodes) { struct reiserfs_journal_cnode *head ; int i ; if (num_cnodes <= 0) { return NULL ; } head = vmalloc(num_cnodes * sizeof(struct reiserfs_journal_cnode)) ; if (!head) { return NULL ; } memset(head, 0, num_cnodes * sizeof(struct reiserfs_journal_cnode)) ; head[0].prev = NULL ; head[0].next = head + 1 ; for (i = 1 ; i < num_cnodes; i++) { head[i].prev = head + (i - 1) ; head[i].next = head + (i + 1) ; /* if last one, overwrite it after the if */ } head[num_cnodes -1].next = NULL ; return head ;}/*** pulls a cnode off the free list, or returns NULL on failure */static struct reiserfs_journal_cnode *get_cnode(struct super_block *p_s_sb) { struct reiserfs_journal_cnode *cn ; reiserfs_check_lock_depth("get_cnode") ; if (SB_JOURNAL(p_s_sb)->j_cnode_free <= 0) { return NULL ; } SB_JOURNAL(p_s_sb)->j_cnode_used++ ; SB_JOURNAL(p_s_sb)->j_cnode_free-- ; cn = SB_JOURNAL(p_s_sb)->j_cnode_free_list ; if (!cn) { return cn ; } if (cn->next) { cn->next->prev = NULL ; } SB_JOURNAL(p_s_sb)->j_cnode_free_list = cn->next ; memset(cn, 0, sizeof(struct reiserfs_journal_cnode)) ; return cn ;}/*** returns a cnode to the free list */static void free_cnode(struct super_block *p_s_sb, struct reiserfs_journal_cnode *cn) { reiserfs_check_lock_depth("free_cnode") ; SB_JOURNAL(p_s_sb)->j_cnode_used-- ; SB_JOURNAL(p_s_sb)->j_cnode_free++ ; /* memset(cn, 0, sizeof(struct reiserfs_journal_cnode)) ; */ cn->next = SB_JOURNAL(p_s_sb)->j_cnode_free_list ; if (SB_JOURNAL(p_s_sb)->j_cnode_free_list) { SB_JOURNAL(p_s_sb)->j_cnode_free_list->prev = cn ; } cn->prev = NULL ; /* not needed with the memset, but I might kill the memset, and forget to do this */ SB_JOURNAL(p_s_sb)->j_cnode_free_list = cn ;}static int clear_prepared_bits(struct buffer_head *bh) { clear_bit(BH_JPrepared, &bh->b_state) ; return 0 ;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -