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

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

パッチエントリ

mm_types.h

Index: mm-test-kernel/mm/memory.c
===================================================================
--- mm-test-kernel.orig/mm/memory.c
+++ mm-test-kernel/mm/memory.c
@@ -376,12 +376,15 @@ int __pte_alloc_kernel(pmd_t *pmd, unsig
        return 0;
 }

-static inline void add_mm_rss(struct mm_struct *mm, int file_rss, int anon_rss)
+static inline void
+add_mm_rss(struct mm_struct *mm, int file_rss, int anon_rss, int swap_usage)
 {
        if (file_rss)
                add_mm_counter(mm, file_rss, file_rss);
        if (anon_rss)
                add_mm_counter(mm, anon_rss, anon_rss);
+       if (swap_usage)
+               add_mm_counter(mm, swap_usage, swap_usage);
 } 

add_mm_rss

この関数ではrssのカウンタの増減を行っています。rssはtopやpsでお馴染みのRSS(Resident Set Size)で、プロセスが保持している物理メモリ上のページサイズです。このコードを見る限り、RSSにはfile_rssとanon_rssの2種類があるようなので、呼び出し元関数を追って、これらの値を見てみます。

add_mm_rss関数を呼び出しているのがcopy_on_range関数で、patchをあてた後だとこの関数内で以下のように呼び出しています。
mm/memory.c

        int rss[3];

again:
        rss[2] = rss[1] = rss[0] = 0;
...
                copy_one_pte(dst_mm, src_mm, dst_pte, src_pte, vma, addr, rss);
                progress += 8;
        } while (dst_pte++, src_pte++, addr += PAGE_SIZE, addr != end);

        arch_leave_lazy_mmu_mode();
        spin_unlock(src_ptl);
        pte_unmap_nested(orig_src_pte);
 add_mm_rss(dst_mm, rss[0], rss[1], rss[2]);

rssの値はここで初期化され、その後copy_one_pteという関数内で加算され、add_mm_rss関数に渡されます。copy_one_pte関数を見ると、patchをあてた後のrssの加算は以下2箇所で行われています。
memory.c

                                if (list_empty(&dst_mm->mmlist))
                                        list_add(&dst_mm->mmlist,
                                                 &src_mm->mmlist);
                                spin_unlock(&mmlist_lock);
                        }
 if (!is_migration_entry(entry))
 rss[2]++; else if (is_write_migration_entry(entry) &&
                                        is_cow_mapping(vm_flags)) {
                                /*                                         
                                 * COW mappings require pages in both parent                                                              
                                 * and child to be set to read.            
                                 */
                                make_migration_entry_read(&entry);
        page = vm_normal_page(vma, addr, pte);
        if (page) {
                get_page(page);
                page_dup_rmap(page);
                rss[PageAnon(page)]++;
        }

out_set_pte:
        set_pte_at(dst_mm, addr, dst_pte, pte);

1つ目の方はパッチによって追加された箇所なので後で追うとして、2箇所目の「rss[PageAnon(page)]++」でrss[0]かrss[1]を加算しているようです(patchをあてるまえはrss[2]で宣言されていたので)。PageAnon関数はfile_rssかanon_rssかを判定できるようです。このPageAnon関数を見てみます。
include/linux/mm.h

=>atic inline int PageAnon(struct page *page)
{
        return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
}

page構造体のメンバであるmappingとPAGE_MAPPING_ANONの論理積を取っています。PAGE_MAPPING_ANONの定義は以下となっています。
include/linux/mm.h

/*                                                                         
 * On an anonymous page mapped into a user virtual memory area,            
 * page->mapping points to its anon_vma, not to a struct address_space;    
 * with the PAGE_MAPPING_ANON bit set to distinguish it.                   
 *                                                                         
 * Please note that, confusingly, "page_mapping" refers to the inode       
 * address_space which maps the page from disk; whereas "page_mapped"      
 * refers to user virtual address space into which the page is mapped.     
 */
=>efine PAGE_MAPPING_ANON       1

PAGE_MAPPING_ANONが1なのでpage->mappingのLSBが1の場合は無名ページ、0の場合はファイルページ(という名称で良いのかわかりませんが)となります。procのmanページを見ると、無名マッピングとファイルマッピングという呼び方の方が一般的なのでしょうか。
page構造体のmappingの箇所には以下のコメントが記載されていました。
include/linux/mm_types.h

                struct address_space *mapping;  /* If low bit clear, points to
                                                 * inode address_space, or NULL.                                                          
                                                 * If page mapped as anonymous
                                                 * memory, low bit is set,and                                                             
                                                 * it points to anon_vma object:                                        
                                                 * see PAGE_MAPPING_ANON below.                                                           
                                                 */

LSBがクリア(0)の場合はi-nodeのアドレス空間を示すということで、mmapで行うようなファイルマッピングを意図しているのだと思います。反対に、無名マッピングmalloc関数のようにファイルとは無関係に取るメモリであると考えられます。/proc/(PID)/mapsを見ると、ファイルパスの有無でファイルマッピングなのかどうかをすぐに見極められます。

...
b6d34000-b6d3d000 r-xp 00000000 08:01 712717     /lib/tls/i686/cmov/libnss_files-2.7.so
b6d3d000-b6d3f000 rw-p 00008000 08:01 712717     /lib/tls/i686/cmov/libnss_files-2.7.so
b6d3f000-b6d47000 r-xp 00000000 08:01 712719     /lib/tls/i686/cmov/libnss_nis-2.7.so
b6d47000-b6d49000 rw-p 00007000 08:01 712719     /lib/tls/i686/cmov/libnss_nis-2.7.so
b6d49000-b6d50000 r-xp 00000000 08:01 712715     /lib/tls/i686/cmov/libnss_compat-2.7.so
b6d50000-b6d52000 rw-p 00006000 08:01 712715     /lib/tls/i686/cmov/libnss_compat-2.7.so
b6d52000-b6d53000 ---p 00000000 00:00 0
b6d53000-b7555000 rw-p 00000000 00:00 0
b7555000-b769e000 r-xp 00000000 08:01 712708     /lib/tls/i686/cmov/libc-2.7.so
b769e000-b769f000 r--p 00149000 08:01 712708     /lib/tls/i686/cmov/libc-2.7.so
b769f000-b76a1000 rw-p 0014a000 08:01 712708     /lib/tls/i686/cmov/libc-2.7.so
b76a1000-b76a4000 rw-p 00000000 00:00 0
b76a4000-b76ae000 r-xp 00000000 08:01 696341     /lib/libgcc_s.so.1
b76ae000-b76af000 rw-p 0000a000 08:01 696341     /lib/libgcc_s.so.1
b76af000-b76d2000 r-xp 00000000 08:01 712712     /lib/tls/i686/cmov/libm-2.7.so
b76d2000-b76d4000 rw-p 00023000 08:01 712712     /lib/tls/i686/cmov/libm-2.7.so
...

以上より、file_rssは物理メモリ上のファイルマッピングのページ、anon_rssは物理メモリ上の無名マッピングのページと考えられます。