[커널 16차 스터디] 92주차 질문 사항입니다.

 

안녕하세요.

16기 이파란입니다.

 

try_to_unmap_one() 할 때,

기존 PTE 매핑을 swap_entry 로 바꿔서 이상합니다.

왜 그럴까요?

 

buddy system

    -> slow path memory allocation

        -> memory compaction

            -> try_to_unmap

 

물리 메모리 복사 붙여넣기 이후, 기존 물리 메모리 영역 매핑 해제 부분을

pageAnon 기준으로 보고 있습니다.

 

코드를 보다가 swap 관련 부분이 아니고, 메모리 compaction 시에

anon page 에 대해서는 모두 이 코드를 타는 것 같아서 의문이 들어서 문의드립니다.

 

컴팩션은 v2.6.35-rc1 에 추가되었고,

더 이전에 pageAnon(page) 가 추가되었던 릴리즈(v2.6.7)에서 확인해보니

해당 분기에 대한 처리는 주관적인 생각으로, 의미적으로는 크게 바뀌지 않은 것 같습니다.

 

이렇게 해당 코드는 유구한 전통(?) 을 가지고 있습니다.

 

저희 보는 5.1 그리고 v5.10.37 기준으로도 많이 바뀌지 않아서 

 

v2.6.7

v5.10.37

 

제가 참고해서 리뷰한 해당 부분의 코드입니다.

 

https://gist.github.com/paranlee/441778236268832ebeadea2954dbe803

 

gist embed 가 잘 안되서 우선, 아래에 나눠서 적었습니다.

 

v2.6.7

/** 

 * v2.6.7

 * https://elixir.bootlin.com/linux/v2.6.7/source/mm/rmap.c#L424

 */

/*

 * Subfunctions of try_to_unmap: try_to_unmap_one called

 * repeatedly from either try_to_unmap_anon or try_to_unmap_file.

 */

static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma)

{

    struct mm_struct *mm = vma->vm_mm;

    unsigned long address;

    pgd_t *pgd;

    pmd_t *pmd;

    pte_t *pte;

    pte_t pteval;

    int ret = SWAP_AGAIN;

 

    if (!mm->rss)

        goto out;

    address = vma_address(page, vma);

    if (address == -EFAULT)

        goto out;

 

    /*

     * We need the page_table_lock to protect us from page faults,

     * munmap, fork, etc...

     */

    if (!spin_trylock(&mm->page_table_lock))

        goto out;

 

    pgd = pgd_offset(mm, address);

    if (!pgd_present(*pgd))

        goto out_unlock;

 

    pmd = pmd_offset(pgd, address);

    if (!pmd_present(*pmd))

        goto out_unlock;

 

    pte = pte_offset_map(pmd, address);

    if (!pte_present(*pte))

        goto out_unmap;

 

    if (page_to_pfn(page) != pte_pfn(*pte))

        goto out_unmap;

 

    /*

     * If the page is mlock()d, we cannot swap it out.

     * If it's recently referenced (perhaps page_referenced

     * skipped over this mm) then we should reactivate it.

     */

    if ((vma->vm_flags & (VM_LOCKED|VM_RESERVED)) ||

            ptep_test_and_clear_young(pte)) {

        ret = SWAP_FAIL;

        goto out_unmap;

    }

 

    /*

     * Don't pull an anonymous page out from under get_user_pages.

     * GUP carefully breaks COW and raises page count (while holding

     * page_table_lock, as we have here) to make sure that the page

     * cannot be freed.  If we unmap that page here, a user write

     * access to the virtual address will bring back the page, but

     * its raised count will (ironically) be taken to mean it's not

     * an exclusive swap page, do_wp_page will replace it by a copy

     * page, and the user never get to see the data GUP was holding

     * the original page for.

     */

    if (PageSwapCache(page) &&

        page_count(page) != page->mapcount + 2) {

        ret = SWAP_FAIL;

        goto out_unmap;

    }

 

    /* Nuke the page table entry. */

    flush_cache_page(vma, address);

    pteval = ptep_clear_flush(vma, address, pte);

 

    /* Move the dirty bit to the physical page now the pte is gone. */

    if (pte_dirty(pteval))

        set_page_dirty(page);

 

    if (PageAnon(page)) {

        swp_entry_t entry = { .val = page->private };

        /*

         * Store the swap location in the pte.

         * See handle_pte_fault() ...

         */

        BUG_ON(!PageSwapCache(page));

        swap_duplicate(entry);

        set_pte(pte, swp_entry_to_pte(entry));

        BUG_ON(pte_file(*pte));

    }

 

    mm->rss--;

    BUG_ON(!page->mapcount);

    page->mapcount--;

    page_cache_release(page);

 

out_unmap:

    pte_unmap(pte);

out_unlock:

    spin_unlock(&mm->page_table_lock);

out:

    return ret;

}

 

v5.10.37

/**

 * v5.10.37

 * https://elixir.bootlin.com/linux/v5.10.37/source/mm/rmap.c#L1617

 */

/*

 * @arg: enum ttu_flags will be passed to this argument

 */

static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,

             unsigned long address, void *arg)

{

    struct mm_struct *mm = vma->vm_mm;

    struct page_vma_mapped_walk pvmw = {

        .page = page,

        .vma = vma,

        .address = address,

    };

    pte_t pteval;

    struct page *subpage;

    bool ret = true;

    struct mmu_notifier_range range;

    enum ttu_flags flags = (enum ttu_flags)(long)arg;

 

    // ..

 

    while (page_vma_mapped_walk(&pvmw)) {

 

        // ...

 

        } else if (IS_ENABLED(CONFIG_MIGRATION) &&
                (flags & (TTU_MIGRATION|TTU_SPLIT_FREEZE))) {

            swp_entry_t entry;
            pte_t swp_pte;

            if (arch_unmap_one(mm, vma, address, pteval) < 0) {
                set_pte_at(mm, address, pvmw.pte, pteval);
                ret = false;
                page_vma_mapped_walk_done(&pvmw);
                break;
            }

            /*
             * Store the pfn of the page in a special migration
             * pte. do_swap_page() will wait until the migration
             * pte is removed and then restart fault handling.
             */

            entry = make_migration_entry(subpage,
                    pte_write(pteval));
            swp_pte = swp_entry_to_pte(entry);

            if (pte_soft_dirty(pteval))
                swp_pte = pte_swp_mksoft_dirty(swp_pte);
            if (pte_uffd_wp(pteval))
                swp_pte = pte_swp_mkuffd_wp(swp_pte);
            set_pte_at(mm, address, pvmw.pte, swp_pte);
            /*
             * No need to invalidate here it will synchronize on
             * against the special swap migration pte.
             */

        } else if (PageAnon(page)) {

            swp_entry_t entry = { .val = page_private(subpage) };

            pte_t swp_pte;

 

            // ...

 

            swp_pte = swp_entry_to_pte(entry);

            if (pte_soft_dirty(pteval))

                swp_pte = pte_swp_mksoft_dirty(swp_pte);

            if (pte_uffd_wp(pteval))

                swp_pte = pte_swp_mkuffd_wp(swp_pte);

            set_pte_at(mm, address, pvmw.pte, swp_pte);

            /* Invalidate as we cleared the pte */

            mmu_notifier_invalidate_range(mm, address,

                              address + PAGE_SIZE);

        }

 

            // ...

 

 

discard:

        /*

         * No need to call mmu_notifier_invalidate_range() it has be

         * done above for all cases requiring it to happen under page

         * table lock before mmu_notifier_invalidate_range_end()

         *

         * See Documentation/vm/mmu_notifier.rst

         */

        page_remove_rmap(subpage, PageHuge(page));

        put_page(page);

    }

 

    mmu_notifier_invalidate_range_end(&range);

 

    return ret;

}

 

아래는 의논하면서 본 swap_entry 관련 다이어그램입니다.

 

swap-6a.png

<출처: http://jake.dothome.co.kr/swap-1/>

 

긴 글 끝까지 읽어주셔서 감사합니다.

 

번호 제목 글쓴이 날짜 조회 수
공지 [공지] 프로그래밍 관련 Q&A 게시판 입니다. woos 2016.04.09 11630
513 ARM64 CPU Context Switch [2] 아띨라 2022.07.24 61
512 Nested Interrupt on ARM64 [4] 아띨라 2022.07.24 160
511 vmcore 파일 생성방법 질문 드립니다. [2] sppedpower 2013.08.28 5053
510 인터럽트 발생 관련해서 질문 드리고 싶습니다. [6] 코딩의노예 2020.07.07 635
509 커널 빌드시 최적화 관련 질문 [5] 김개미 2019.12.17 1125
508 mmap후 파일 올리기 [3] 읏짜 2022.01.13 417
» [질문-해결됨] compaction 후 기존 PTE 매핑에 뜬금 스왑 엔트리가 나와요! [12] 이파란 2021.05.17 1754
506 16기 리눅스 커널 스터디 C조 18주차 질문입니다. [3] cien 2019.10.12 10035
505 iamroot선배님들의 조언 부탁드립니다 [2] 이파란 2021.04.15 588
504 오드로이드에 관해 akqk13012 2021.03.26 372
503 critical section에 대한 커널 코드 검증 방법 문의 [7] file mnth 2021.01.09 1352
502 RCU 관련해서 질문 하나 드려봅니다. [2] 코딩의노예 2020.07.03 522
501 멀티스레드 시간측정 질문 file 뇨뇸 2020.05.03 402
500 리눅스 소스 분석 관련 질문 [4] AlanWalker 2020.04.22 612
499 16기 리눅스 커널 스터디 39주차 질문입니다. [6] rnsscman 2020.03.22 528
498 ___reserved_mem_reserve_reg 관련 질문 [4] DEWH 2020.03.09 4188
497 16기 리눅스 커널 스터디 33주차 질문입니다. [5] rnsscman 2020.02.08 288
496 setup_machine_fdt() -> fixmap_remap_fdt() 질문입니다. [2] daeroro 2019.12.24 230
495 early_fixmap_init 질문 [3] file 에러 2019.12.17 209
494 16기 리눅스 커널 스터디 27주차 질문입니다. [1] daeroro 2019.12.18 415
XE Login