プロセス毎のswap使用量を計測するパッチを読む その5

「プロセス毎のswap使用量を計測するパッチを読む その4」の続き。

copy_one_pte

copy_one_pte関数の続き。前回はページエントリがスワップページであった場合でしたが、今回は物理ページ上にあるかどうかは関係ない部分の処理となります。

mm/memory.c

        /*                                                                                             
         * If it's a COW mapping, write protect it both                                                
         * in the parent and the child                                                                 
         */
        if (is_cow_mapping(vm_flags)) {
                ptep_set_wrprotect(src_mm, addr, src_pte);
                pte = pte_wrprotect(pte);
        }

        /*                                                                                             
         * If it's a shared mapping, mark it clean in                                                  
         * the child                                                                                   
         */
        if (vm_flags & VM_SHARED)
                pte = pte_mkclean(pte);
        pte = pte_mkold(pte);

コメントには、COW(CopyOnWrite)マッピングであれば親子両方にwrite protectをかける、とあります。is_cow_mapping関数にvm_flagsを渡していますが、struct vm_area_structを先に見てみます。

vm_area_struct

vm_flagsはvm_area_structのメンバで、vm_flagsの定義はmm.hにあります。vm_area_sructはmm_structのmmapメンバとして参照されており、プロセス空間を構成する論理的な領域になります。vm_flagsはその領域の属性を表すのでしょう。
include/linux/mm.h:

/*                                                                                                     
 * vm_flags in vm_area_struct, see mm_types.h.                                                         
 */
#define VM_READ         0x00000001      /* currently active flags */
#define VM_WRITE        0x00000002
#define VM_EXEC         0x00000004
#define VM_SHARED       0x00000008

/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */
#define VM_MAYREAD      0x00000010      /* limits for mprotect() etc */
#define VM_MAYWRITE     0x00000020
#define VM_MAYEXEC      0x00000040
#define VM_MAYSHARE     0x00000080

#define VM_GROWSDOWN    0x00000100      /* general info on the segment */
#define VM_GROWSUP      0x00000200
#define VM_PFNMAP       0x00000400      /* Page-ranges managed without "struct page", just pure PFN */
#define VM_DENYWRITE    0x00000800      /* ETXTBSY on write attempts.. */

#define VM_EXECUTABLE   0x00001000
#define VM_LOCKED       0x00002000
#define VM_IO           0x00004000      /* Memory mapped I/O or similar */
                                        /* Used by sys_madvise() */
#define VM_SEQ_READ     0x00008000      /* App will access data sequentially */
#define VM_RAND_READ    0x00010000      /* App will not benefit from clustered reads */

#define VM_DONTCOPY     0x00020000      /* Do not copy this vma on fork */
#define VM_DONTEXPAND   0x00040000      /* Cannot expand with mremap() */
#define VM_RESERVED     0x00080000      /* Count as reserved_vm like IO */
#define VM_ACCOUNT      0x00100000      /* Is a VM accounted object */
#define VM_NORESERVE    0x00200000      /* should the VM suppress accounting */
#define VM_HUGETLB      0x00400000      /* Huge TLB Page VM */
#define VM_NONLINEAR    0x00800000      /* Is non-linear (remap_file_pages) */
#define VM_MAPPED_COPY  0x01000000      /* T if mapped copy of data (nommu mmap) */
#define VM_INSERTPAGE   0x02000000      /* The vma has had "vm_insert_page()" done on it */
#define VM_ALWAYSDUMP   0x04000000      /* Always include in core dumps */

#define VM_CAN_NONLINEAR 0x08000000     /* Has ->fault & does nonlinear pages */
#define VM_MIXEDMAP     0x10000000      /* Can contain "struct page" and pure PFN pages */
#define VM_SAO          0x20000000      /* Strong Access Ordering (powerpc) */
#define VM_PFN_AT_MMAP  0x40000000      /* PFNMAP vma that is fully mapped at mmap time */
#define VM_MERGEABLE    0x80000000      /* KSM may merge identical pages */

#ifndef VM_STACK_DEFAULT_FLAGS          /* arch can override this */
#define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
#endif

#ifdef CONFIG_STACK_GROWSUP
#define VM_STACK_FLAGS  (VM_GROWSUP | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
#else
#define VM_STACK_FLAGS  (VM_GROWSDOWN | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
#endif

#define VM_READHINTMASK                 (VM_SEQ_READ | VM_RAND_READ)
#define VM_ClearReadHint(v)             (v)->vm_flags &= ~VM_READHINTMASK
#define VM_NormalReadHint(v)            (!((v)->vm_flags & VM_READHINTMASK))
#define VM_SequentialReadHint(v)        ((v)->vm_flags & VM_SEQ_READ)
#define VM_RandomReadHint(v)            ((v)->vm_flags & VM_RAND_READ)

/*                                                                                                     
 * special vmas that are non-mergable, non-mlock()able                                                 
 */
#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP)

is_cow_mapping

is_cow_mapping関数の実装は以下のようになっていました。
mm/memory.c

static inline int is_cow_mapping(unsigned int flags)
{
        return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
}

write可能で且つ共有ではない領域は、CopyOnWriteの対象になるということになります。
cow mappingだった場合に呼ばれるptep_set_wrprotect関数、pte_wrprotect関数は、(今回CScopeに食わせてない)アーキテクチャ依存の部分なので割愛します。どちらもページテーブルエントリを書き込み不可に設定しているようです。
さらにSHAREDであればページテーブルエントリのダーティフラグ(そのページが書き込まれたかどうか)とアクセスフラグ(アクセスされたかどうか)をクリアしています。(pte_mkclean関数、pte_mkold関数)

pte_〜関数の詳細については、以下のページを参考にさせてもらいました。

「LinuxKernelHackJapan」
http://hira-consulting.com/wiki/index.php?%A4%E8%A4%A6%A4%B3%A4%BD