writeback機制源碼分析

原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://alanwu.blog.51cto.com/3652632/1110046

 

writeback相關數據結構
 
與writeback相關的數據結構主要有:
1,backing_dev_info,該數據結構描述了backing_dev的所有信息,通常塊設備的request queue中會包含backing_dev對象。
2,bdi_writeback,該數據結構封裝了writeback的內核線程以及需要操作的inode隊列。
3,wb_writeback_work,該數據結構封裝了writeback的工作任務。
 
各數據結構之間的關係如下圖所示:
 

 

下面對各個數據結構做簡要介紹。
 
bdi information
 
bdi對象在塊設備添加的時候需要註冊到系統的bdi隊列中。對於ext3而言,在mount的時候需要將底層塊設備的bdi對象聯繫到ext3 root_inode中。bdi對象數據結構定義如下:
  1. struct backing_dev_info {  
  2.     struct list_head bdi_list;  
  3.     unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */  
  4.     unsigned long state;    /* Always use atomic bitops on this */  
  5.     unsigned int capabilities; /* Device capabilities */  
  6.     congested_fn *congested_fn; /* Function pointer if device is md/dm */  
  7.     void *congested_data;   /* Pointer to aux data for congested func */  
  8.  
  9.     char *name;  
  10.  
  11.     struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];  
  12.  
  13.     unsigned long bw_time_stamp;    /* last time write bw is updated */  
  14.     unsigned long dirtied_stamp;  
  15.     unsigned long written_stamp;    /* pages written at bw_time_stamp */  
  16.     unsigned long write_bandwidth;  /* the estimated write bandwidth */  
  17.     unsigned long avg_write_bandwidth; /* further smoothed write bw */  
  18.  
  19.     /*  
  20.      * The base dirty throttle rate, re-calculated on every 200ms.  
  21.      * All the bdi tasks' dirty rate will be curbed under it.  
  22.      * @dirty_ratelimit tracks the estimated @balanced_dirty_ratelimit  
  23.      * in small steps and is much more smooth/stable than the latter.  
  24.      */  
  25.     unsigned long dirty_ratelimit;  
  26.     unsigned long balanced_dirty_ratelimit;  
  27.  
  28.     struct prop_local_percpu completions;  
  29.     int dirty_exceeded;  
  30.  
  31.     unsigned int min_ratio;  
  32.     unsigned int max_ratio, max_prop_frac;  
  33.  
  34.     struct bdi_writeback wb;  /* default writeback info for this bdi,writeback對象 */  
  35.     spinlock_t wb_lock;   /* protects work_list */  
  36.  
  37.     /* 任務鏈表 */  
  38.     struct list_head work_list;  
  39.  
  40.     struct device *dev;  
  41.     /* 在laptop模式下應用的定時器 */  
  42.     struct timer_list laptop_mode_wb_timer;  
  43.  
  44. #ifdef CONFIG_DEBUG_FS  
  45.     struct dentry *debug_dir;  
  46.     struct dentry *debug_stats;  
  47. #endif  
  48. }; 

 

在bdi數據結構中定義了一個writeback對象,該對象是對writeback內核線程的描述,並且封裝了需要處理的inode隊列。在bdi數據結構中有一條work_list,該work隊列維護了writeback內核線程需要處理的任務。如果該隊列上沒有work可以處理,那麼writeback內核線程將會睡眠等待。
writeback
 
writeback對象封裝了內核線程task以及需要處理的inode隊列。當page cache/buffer cache需要刷新radix tree上的inode時,可以將該inode掛載到writeback對象的b_dirty隊列上,然後喚醒writeback線程。在處理過程中,inode會被移到b_io隊列上進行處理。多條鏈表的方式可以降低多線程之間的資源共享。writeback數據結構具體定義如下:

 

  1. struct bdi_writeback {  
  2.     struct backing_dev_info *bdi;   /* our parent bdi */  
  3.     unsigned int nr;  
  4.  
  5.     unsigned long last_old_flush;   /* last old data flush */  
  6.     unsigned long last_active;  /* last time bdi thread was active */  
  7.  
  8.     struct task_struct *task;   /* writeback thread */  
  9.     struct timer_list wakeup_timer; /* used for delayed bdi thread wakeup */  
  10.     struct list_head b_dirty;   /* dirty inodes */  
  11.     struct list_head b_io;      /* parked for writeback */  
  12.     struct list_head b_more_io; /* parked for more writeback */  
  13.     spinlock_t list_lock;       /* protects the b_* lists */  
  14. }; 

writeback work

 
wb_writeback_work數據結構是對writeback任務的封裝,不同的任務可以採用不同的刷新策略。writeback線程的處理對象就是writeback_work。如果writeback_work隊列爲空,那麼內核線程就可以睡眠了。Writeback_work的數據結構定義如下:
  1. struct wb_writeback_work {  
  2.     long nr_pages;  
  3.     struct super_block *sb; /* superblock對象 */  
  4.     unsigned long *older_than_this;  
  5.     enum writeback_sync_modes sync_mode;  
  6.     unsigned int tagged_writepages:1;  
  7.     unsigned int for_kupdate:1;  
  8.     unsigned int range_cyclic:1;  
  9.     unsigned int for_background:1;  
  10.     enum wb_reason reason;      /* why was writeback initiated? */  
  11.       
  12.     struct list_head list;      /* pending work list,鏈入bdi-> work_list隊列 */  
  13.     struct completion *done;    /* set if the caller waits,work完成時通知調用者 */  
  14. }; 

 

writeback主要函數分析
 
writeback機制的主要函數包括如下兩個方面:
1,管理bdi對象並且fork相應的writeback內核線程處理cache數據的刷新工作。
2,writeback內核線程處理函數,實現dirty page的刷新操作
 
writeback線程管理
 
Linux中有一個內核守護線程,該線程用來管理系統bdi隊列,並且負責爲block device創建writeback thread。當bdi中有dirty page並且還沒有爲bdi分配內核線程的時候,bdi_forker_thread程序會爲其分配線程資源;當一個writeback線程長時間處於空閒狀態時,bdi_forker_thread程序會釋放該線程資源。
 
writeback線程管理程序分析如下:
  1. static int bdi_forker_thread(void *ptr)  
  2. {  
  3.     struct bdi_writeback *me = ptr;  
  4.  
  5.     current->flags |= PF_SWAPWRITE;  
  6.     set_freezable();  
  7.  
  8.     /*  
  9.      * Our parent may run at a different priority, just set us to normal  
  10.      */  
  11.     set_user_nice(current, 0);  
  12.  
  13.     for (;;) {  
  14.         struct task_struct *task = NULL;  
  15.         struct backing_dev_info *bdi;  
  16.         enum {  
  17.             NO_ACTION,   /* Nothing to do */  
  18.             FORK_THREAD, /* Fork bdi thread */  
  19.             KILL_THREAD, /* Kill inactive bdi thread */  
  20.         } action = NO_ACTION;  
  21.  
  22.         /*  
  23.          * Temporary measure, we want to make sure we don't see  
  24.          * dirty data on the default backing_dev_info  
  25.          */  
  26.         if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list)) {  
  27.             del_timer(&me->wakeup_timer);  
  28.             wb_do_writeback(me, 0);  
  29.         }  
  30.  
  31.         spin_lock_bh(&bdi_lock);  
  32.         /*  
  33.          * In the following loop we are going to check whether we have  
  34.          * some work to do without any synchronization with tasks  
  35.          * waking us up to do work for them. Set the task state here  
  36.          * so that we don't miss wakeups after verifying conditions.  
  37.          */  
  38.         set_current_state(TASK_INTERRUPTIBLE);  
  39.         /* 遍歷所有的bdi對象,檢查這些bdi是否存在髒數據,如果有髒數據,那麼需要爲其fork線程,然後做writeback操作 */  
  40.         list_for_each_entry(bdi, &bdi_list, bdi_list) {  
  41.             bool have_dirty_io;  
  42.  
  43.             if (!bdi_cap_writeback_dirty(bdi) ||  
  44.                  bdi_cap_flush_forker(bdi))  
  45.                 continue;  
  46.  
  47.             WARN(!test_bit(BDI_registered, &bdi->state),  
  48.                  "bdi %p/%s is not registered!\n", bdi, bdi->name);  
  49.             /* 檢查是否存在髒數據 */  
  50.             have_dirty_io = !list_empty(&bdi->work_list) ||  
  51.                     wb_has_dirty_io(&bdi->wb);  
  52.  
  53.             /*  
  54.              * If the bdi has work to do, but the thread does not  
  55.              * exist - create it.  
  56.              */  
  57.             if (!bdi->wb.task && have_dirty_io) {  
  58.                 /*  
  59.                  * Set the pending bit - if someone will try to  
  60.                  * unregister this bdi - it'll wait on this bit.  
  61.                  */  
  62.                 /* 如果有髒數據,並且不存在線程,那麼接下來做線程的FORK操作 */  
  63.                 set_bit(BDI_pending, &bdi->state);  
  64.                 action = FORK_THREAD;  
  65.                 break;  
  66.             }  
  67.  
  68.             spin_lock(&bdi->wb_lock);  
  69.  
  70.             /*  
  71.              * If there is no work to do and the bdi thread was  
  72.              * inactive long enough - kill it. The wb_lock is taken  
  73.              * to make sure no-one adds more work to this bdi and  
  74.              * wakes the bdi thread up.  
  75.              */  
  76.             /* 如果一個bdi長時間沒有髒數據,那麼執行線程的KILL操作,結束掉該bdi對應的writeback線程 */  
  77.             if (bdi->wb.task && !have_dirty_io &&  
  78.                 time_after(jiffies, bdi->wb.last_active +  
  79.                         bdi_longest_inactive())) {  
  80.                 task = bdi->wb.task;  
  81.                 bdi->wb.task = NULL;  
  82.                 spin_unlock(&bdi->wb_lock);  
  83.                 set_bit(BDI_pending, &bdi->state);  
  84.                 action = KILL_THREAD;  
  85.                 break;  
  86.             }  
  87.             spin_unlock(&bdi->wb_lock);  
  88.         }  
  89.         spin_unlock_bh(&bdi_lock);  
  90.  
  91.         /* Keep working if default bdi still has things to do */  
  92.         if (!list_empty(&me->bdi->work_list))  
  93.             __set_current_state(TASK_RUNNING);  
  94.         /* 執行線程的FORK和KILL操作 */  
  95.         switch (action) {  
  96.         case FORK_THREAD:  
  97.             /* FORK一個bdi_writeback_thread線程,該線程的名字爲flush-major:minor */  
  98.             __set_current_state(TASK_RUNNING);  
  99.             task = kthread_create(bdi_writeback_thread, &bdi->wb,  
  100.                           "flush-%s", dev_name(bdi->dev));  
  101.             if (IS_ERR(task)) {  
  102.                 /*  
  103.                  * If thread creation fails, force writeout of  
  104.                  * the bdi from the thread. Hopefully 1024 is  
  105.                  * large enough for efficient IO.  
  106.                  */  
  107.                 writeback_inodes_wb(&bdi->wb, 1024,  
  108.                             WB_REASON_FORKER_THREAD);  
  109.             } else {  
  110.                 /*  
  111.                  * The spinlock makes sure we do not lose  
  112.                  * wake-ups when racing with 'bdi_queue_work()'.  
  113.                  * And as soon as the bdi thread is visible, we  
  114.                  * can start it.  
  115.                  */  
  116.                 spin_lock_bh(&bdi->wb_lock);  
  117.                 bdi->wb.task = task;  
  118.                 spin_unlock_bh(&bdi->wb_lock);  
  119.                 wake_up_process(task);  
  120.             }  
  121.             bdi_clear_pending(bdi);  
  122.             break;  
  123.  
  124.         case KILL_THREAD:  
  125.             /* KILL一個線程 */  
  126.             __set_current_state(TASK_RUNNING);  
  127.             kthread_stop(task);  
  128.             bdi_clear_pending(bdi);  
  129.             break;  
  130.  
  131.         case NO_ACTION:  
  132.             /* 如果沒有可執行的動作,那麼調度本線程睡眠一段時間 */  
  133.             if (!wb_has_dirty_io(me) || !dirty_writeback_interval)  
  134.                 /*  
  135.                  * There are no dirty data. The only thing we  
  136.                  * should now care about is checking for  
  137.                  * inactive bdi threads and killing them. Thus,  
  138.                  * let's sleep for longer time, save energy and  
  139.                  * be friendly for battery-driven devices.  
  140.                  */  
  141.                 schedule_timeout(bdi_longest_inactive());  
  142.             else  
  143.                 schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10));  
  144.             try_to_freeze();  
  145.             break;  
  146.         }  
  147.     }  
  148.  
  149.     return 0;  

writeback線程

 
writeback線程是bdi_forker_thread 創建的,該線程的任務就是處理等待的數據回刷任務。線程處理函數爲bdi_writeback_thread,其會調用wb_do_writeback函數完成具體操作,該函數分析如下:
  1. long wb_do_writeback(struct bdi_writeback *wb, int force_wait)  
  2. {  
  3.     struct backing_dev_info *bdi = wb->bdi;  
  4.     struct wb_writeback_work *work;  
  5.     long wrote = 0;  
  6.  
  7.     set_bit(BDI_writeback_running, &wb->bdi->state);  
  8.     /* 處理等待的work,所有等待work pengding在bdi->work_list上 */  
  9.     while ((work = get_next_work_item(bdi)) != NULL) {  
  10.         /*  
  11.          * Override sync mode, in case we must wait for completion  
  12.          * because this thread is exiting now.  
  13.          */  
  14.         if (force_wait)  
  15.             work->sync_mode = WB_SYNC_ALL;  
  16.  
  17.         trace_writeback_exec(bdi, work);  
  18.         /* 調用wb_writeback函數處理相應的inode */  
  19.         wrote += wb_writeback(wb, work);  
  20.  
  21.         /*  
  22.          * Notify the caller of completion if this is a synchronous  
  23.          * work item, otherwise just free it.  
  24.          */  
  25.         /* 通知上層軟件,相應的work已經完成 */  
  26.         if (work->done)  
  27.             complete(work->done);  
  28.         else  
  29.             kfree(work);  
  30.     }  
  31.  
  32.     /*  
  33.      * Check for periodic writeback, kupdated() style  
  34.      */  
  35.     /* 處理週期性的dirty page刷新作業,buffer cache就會走這條路徑,在下面的函數中會創建work,並且調用wb_writeback函數進行處理 */  
  36.     wrote += wb_check_old_data_flush(wb);  
  37.     wrote += wb_check_background_flush(wb);  
  38.     clear_bit(BDI_writeback_running, &wb->bdi->state);  
  39.  
  40.     return wrote;  

 

小結
 
本文在linux-3.2的基礎上對writeback代碼進行了瀏覽。整體上來講,writeback機制是比較簡單的,其核心是通過一個常駐內核線程爲bdi對象分配writeback線程,實現對cache中dirty page的數據回刷。

本文出自 “存儲之道” 博客,請務必保留此出處http://alanwu.blog.51cto.com/3652632/1110046

發佈了1 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章