writeback 메커니즘 소스 코드 분석

12960 단어
writeback 관련 데이터 구조
writeback 과 관련 된 데이터 구 조 는 주로 다음 과 같다.
1,backing_dev_info, 이 데이터 구 조 는 backing 을 묘사 합 니 다.dev 의 모든 정보, 일반 블록 장치 의 request quue 에는 backing 이 포함 되 어 있 습 니 다.dev 대상.
2,bdi_writeback, 이 데이터 구 조 는 writeback 의 커 널 스 레 드 와 조작 해 야 할 inode 대기 열 을 패키지 합 니 다.
3,wb_writeback_work, 이 데이터 구 조 는 writeback 의 작업 임 무 를 패키지 합 니 다.
각 데이터 구조 간 의 관 계 는 다음 그림 과 같다.
다음은 각 데이터 구조 에 대해 간략하게 소개 한다.
bdi information
bdi 대상 은 블록 장치 가 추 가 될 때 시스템 의 bdi 대기 열 에 등록 해 야 합 니 다.ext 3 의 경우 mount 에 있 을 때 바 텀 블록 장치 의 bdi 대상 을 ext 3 root 에 연결 해 야 합 니 다.inode 중.bdi 대상 데이터 구조 정 의 는 다음 과 같 습 니 다.
 
struct backing_dev_info {  
    struct list_head bdi_list;  
    unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */
    unsigned long state;    /* Always use atomic bitops on this */
    unsigned int capabilities; /* Device capabilities */
    congested_fn *congested_fn; /* Function pointer if device is md/dm */
    void *congested_data;   /* Pointer to aux data for congested func */
     
    char *name;  
     
    struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];  
     
    unsigned long bw_time_stamp;    /* last time write bw is updated */
    unsigned long dirtied_stamp;  
    unsigned long written_stamp;    /* pages written at bw_time_stamp */
    unsigned long write_bandwidth;  /* the estimated write bandwidth */
    unsigned long avg_write_bandwidth; /* further smoothed write bw */
     
    /*  
     * The base dirty throttle rate, re-calculated on every 200ms.  
     * All the bdi tasks' dirty rate will be curbed under it.  
     * @dirty_ratelimit tracks the estimated @balanced_dirty_ratelimit  
     * in small steps and is much more smooth/stable than the latter.  
     */
    unsigned long dirty_ratelimit;  
    unsigned long balanced_dirty_ratelimit;  
     
    struct prop_local_percpu completions;  
    int dirty_exceeded;  
     
    unsigned int min_ratio;  
    unsigned int max_ratio, max_prop_frac;  
     
    struct bdi_writeback wb;  /* default writeback info for this bdi,writeback   */
    spinlock_t wb_lock;   /* protects work_list */
     
    /*      */
    struct list_head work_list;  
     
    struct device *dev;  
    /*  laptop          */
    struct timer_list laptop_mode_wb_timer;  
     
#ifdef CONFIG_DEBUG_FS  
    struct dentry *debug_dir;  
    struct dentry *debug_stats;  
#endif  
};

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 데이터 구 조 는 다음 과 같이 구체 적 으로 정의 합 니 다.
 
struct bdi_writeback {  
    struct backing_dev_info *bdi;   /* our parent bdi */
    unsigned int nr;  
     
    unsigned long last_old_flush;   /* last old data flush */
    unsigned long last_active;  /* last time bdi thread was active */
     
    struct task_struct *task;   /* writeback thread */
    struct timer_list wakeup_timer; /* used for delayed bdi thread wakeup */
    struct list_head b_dirty;   /* dirty inodes */
    struct list_head b_io;      /* parked for writeback */
    struct list_head b_more_io; /* parked for more writeback */
    spinlock_t list_lock;       /* protects the b_* lists */
};

 
writeback work
wb_writeback_work 데이터 구 조 는 writeback 작업 에 대한 패키지 로 서로 다른 작업 은 서로 다른 리 셋 전략 을 사용 할 수 있 습 니 다.writeback 스 레 드 의 처리 대상 은 writeback 입 니 다.work.하면, 만약, 만약...work 대기 열 이 비어 있 으 면 커 널 스 레 드 가 잠 을 잘 수 있 습 니 다.Writeback_work 의 데이터 구조 정 의 는 다음 과 같다.
 
struct wb_writeback_work {  
    long nr_pages;  
    struct super_block *sb; /* superblock   */
    unsigned long *older_than_this;  
    enum writeback_sync_modes sync_mode;  
    unsigned int tagged_writepages:1;  
    unsigned int for_kupdate:1;  
    unsigned int range_cyclic:1;  
    unsigned int for_background:1;  
    enum wb_reason reason;      /* why was writeback initiated? */
          
    struct list_head list;      /* pending work list,  bdi-> work_list   */
    struct completion *done;    /* set if the caller waits,work         */
};

 
writeback 주요 함수 분석
writeback 메커니즘 의 주요 함 수 는 다음 과 같은 두 가지 측면 을 포함한다.
1. bdi 대상 을 관리 하고 fork 에 해당 하 는 writeback 커 널 스 레 드 처리 cache 데이터 의 리 셋 작업.
2, writeback 커 널 스 레 드 처리 함수, dirty page 의 리 셋 작업 실현
writeback 스 레 드 관리
Linux 에 커 널 데 몬 스 레 드 가 있 습 니 다. 이 스 레 드 는 시스템 bdi 대기 열 을 관리 하고 block device 에 writeback thread 를 만 드 는 데 사 용 됩 니 다.bdi 에 dirty page 가 있 고 bdi 에 커 널 스 레 드 를 할당 하지 않 았 을 때 bdiforker_thread 프로그램 은 스 레 드 자원 을 분배 합 니 다.writeback 스 레 드 가 오랫동안 비어 있 을 때 bdiforker_thread 프로그램 은 이 스 레 드 자원 을 방출 합 니 다.
writeback 스 레 드 관리 프로그램 분석 은 다음 과 같 습 니 다.
static int bdi_forker_thread(void *ptr)  
{  
    struct bdi_writeback *me = ptr;  
     
    current->flags |= PF_SWAPWRITE;  
    set_freezable();  
     
    /*  
     * Our parent may run at a different priority, just set us to normal  
     */
    set_user_nice(current, 0);  
     
    for (;;) {  
        struct task_struct *task = NULL;  
        struct backing_dev_info *bdi;  
        enum {  
            NO_ACTION,   /* Nothing to do */
            FORK_THREAD, /* Fork bdi thread */
            KILL_THREAD, /* Kill inactive bdi thread */
        } action = NO_ACTION;  
     
        /*  
         * Temporary measure, we want to make sure we don't see  
         * dirty data on the default backing_dev_info  
         */
        if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list)) {  
            del_timer(&me->wakeup_timer);  
            wb_do_writeback(me, 0);  
        }  
     
        spin_lock_bh(&bdi_lock);  
        /*  
         * In the following loop we are going to check whether we have  
         * some work to do without any synchronization with tasks  
         * waking us up to do work for them. Set the task state here  
         * so that we don't miss wakeups after verifying conditions.  
         */
        set_current_state(TASK_INTERRUPTIBLE);  
        /*      bdi  ,    bdi       ,      ,      fork  ,   writeback   */
        list_for_each_entry(bdi, &bdi_list, bdi_list) {  
            bool have_dirty_io;  
     
            if (!bdi_cap_writeback_dirty(bdi) ||  
                 bdi_cap_flush_forker(bdi))  
                continue;  
     
            WARN(!test_bit(BDI_registered, &bdi->state),  
                 "bdi %p/%s is not registered!
", bdi, bdi->name); /* */ have_dirty_io = !list_empty(&bdi->work_list) || wb_has_dirty_io(&bdi->wb); /* * If the bdi has work to do, but the thread does not * exist - create it. */ if (!bdi->wb.task && have_dirty_io) { /* * Set the pending bit - if someone will try to * unregister this bdi - it'll wait on this bit. */ /* , , FORK */ set_bit(BDI_pending, &bdi->state); action = FORK_THREAD; break; } spin_lock(&bdi->wb_lock); /* * If there is no work to do and the bdi thread was * inactive long enough - kill it. The wb_lock is taken * to make sure no-one adds more work to this bdi and * wakes the bdi thread up. */ /* bdi , KILL , bdi writeback */ if (bdi->wb.task && !have_dirty_io && time_after(jiffies, bdi->wb.last_active + bdi_longest_inactive())) { task = bdi->wb.task; bdi->wb.task = NULL; spin_unlock(&bdi->wb_lock); set_bit(BDI_pending, &bdi->state); action = KILL_THREAD; break; } spin_unlock(&bdi->wb_lock); } spin_unlock_bh(&bdi_lock); /* Keep working if default bdi still has things to do */ if (!list_empty(&me->bdi->work_list)) __set_current_state(TASK_RUNNING); /* FORK KILL */ switch (action) { case FORK_THREAD: /* FORK bdi_writeback_thread , flush-major:minor */ __set_current_state(TASK_RUNNING); task = kthread_create(bdi_writeback_thread, &bdi->wb, "flush-%s", dev_name(bdi->dev)); if (IS_ERR(task)) { /* * If thread creation fails, force writeout of * the bdi from the thread. Hopefully 1024 is * large enough for efficient IO. */ writeback_inodes_wb(&bdi->wb, 1024, WB_REASON_FORKER_THREAD); } else { /* * The spinlock makes sure we do not lose * wake-ups when racing with 'bdi_queue_work()'. * And as soon as the bdi thread is visible, we * can start it. */ spin_lock_bh(&bdi->wb_lock); bdi->wb.task = task; spin_unlock_bh(&bdi->wb_lock); wake_up_process(task); } bdi_clear_pending(bdi); break; case KILL_THREAD: /* KILL */ __set_current_state(TASK_RUNNING); kthread_stop(task); bdi_clear_pending(bdi); break; case NO_ACTION: /* , */ if (!wb_has_dirty_io(me) || !dirty_writeback_interval) /* * There are no dirty data. The only thing we * should now care about is checking for * inactive bdi threads and killing them. Thus, * let's sleep for longer time, save energy and * be friendly for battery-driven devices. */ schedule_timeout(bdi_longest_inactive()); else schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10)); try_to_freeze(); break; } } return 0; }

writeback 스 레 드
writeback 스 레 드 는 bdiforker_thread 가 만 든 이 스 레 드 의 작업 은 기다 리 는 데이터 리 턴 작업 을 처리 하 는 것 입 니 다.스 레 드 처리 함 수 는 bdiwriteback_thread, wb 호출do_writeback 함수 가 구체 적 인 조작 을 완 료 했 습 니 다. 이 함수 분석 은 다음 과 같 습 니 다.
long wb_do_writeback(struct bdi_writeback *wb, int force_wait)  
{  
    struct backing_dev_info *bdi = wb->bdi;  
    struct wb_writeback_work *work;  
    long wrote = 0;  
     
    set_bit(BDI_writeback_running, &wb->bdi->state);  
    /*      work,    work pengding bdi->work_list  */
    while ((work = get_next_work_item(bdi)) != NULL) {  
        /*  
         * Override sync mode, in case we must wait for completion  
         * because this thread is exiting now.  
         */
        if (force_wait)  
            work->sync_mode = WB_SYNC_ALL;  
     
        trace_writeback_exec(bdi, work);  
        /*   wb_writeback       inode */
        wrote += wb_writeback(wb, work);  
     
        /*  
         * Notify the caller of completion if this is a synchronous  
         * work item, otherwise just free it.  
         */
        /*       ,   work     */
        if (work->done)  
            complete(work->done);  
        else
            kfree(work);  
    }  
     
    /*  
     * Check for periodic writeback, kupdated() style  
     */
    /*       dirty page    ,buffer cache       ,          work,    wb_writeback       */
    wrote += wb_check_old_data_flush(wb);  
    wrote += wb_check_background_flush(wb);  
    clear_bit(BDI_writeback_running, &wb->bdi->state);  
     
    return wrote;  
}

작은 매듭
본 고 는 linux - 3.2 를 바탕 으로 writeback 코드 를 조회 했다.전체적으로 보면 writeback 체 제 는 비교적 간단 하 다. 그 핵심 은 상주 커 널 스 레 드 를 통 해 bdi 대상 에 writeback 스 레 드 를 분배 하여 cache 중의 dirty page 에 대한 데이터 리 턴 을 실현 하 는 것 이다.

좋은 웹페이지 즐겨찾기