6.828: Operating System Engineering (49)

ええと、page_insert の先頭部分を以下にしてみた。

int
page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) 
{
    // Fill this function in
    pte_t *tmp = pgdir_walk(pgdir, va, 0);
    if (tmp != NULL && *tmp == page2pa(pp)) {
        page_remove(pgdir, va);
    }

    pte_t *pte = pgdir_walk(pgdir, va, ~0);
    if (pte == NULL)
        return -E_NO_MEM;

まだ駄目な模様。ええとこれは tmp じゃなくて pgdir_walk の戻りを PADDR なフィルタをかけないと駄目なのかな。

    pte_t *tmp = (pte_t *)PADDR(pgdir_walk(pgdir, va, 0));

違う模様。printf デバッグしながら以下なナニがでっち上がりました。

int
page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) 
{
    // Fill this function in
    pte_t *tmp = pgdir_walk(pgdir, va, 0);

    if (tmp != NULL && PTE_ADDR(*tmp) == page2pa(pp)) {
        page_remove(pgdir, va);
    }

    pte_t *pte = pgdir_walk(pgdir, va, ~0);

これで pp_ref な assert はパス。直後の以下でコケてます。

    // pp2 should NOT be on the free list
    // could happen in ref counts are handled sloppily in page_insert
    assert(page_alloc(&pp) == -E_NO_MEM);

これ、どーゆー意味だろうな。page_remove 手続きが微妙なのかな。なんかソレっぽいコメントが page_insert にあるんですが微妙。

// Corner-case hint: Make sure to consider what happens when the same 
// pp is re-inserted at the same virtual address in the same pgdir.

失敗する assert の直前で page_free_list.lh_first の値を printf してみたら NULL ではない模様。しかしなんで以下なナニで二度目に NG になるんだろ。

    // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
    assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
    assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
    assert(pp2->pp_ref == 1);

    // should be no free memory
    assert(page_alloc(&pp) == -E_NO_MEM);

    // should be able to map pp2 at PGSIZE because it's already there
    assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
    assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
    assert(pp2->pp_ref == 1);

    // pp2 should NOT be on the free list
    // could happen in ref counts are handled sloppily in page_insert
    assert(page_alloc(&pp) == -E_NO_MEM);

最初のソレは pte が存在してるのでセイフな認識。次のナニは page_remove するケイスのはずなのですが、page_remove が微妙なのかな。
そもそも

//   - If there is already a page mapped at 'va', it should be page_remove()d.

の意味を正確に理解できてないのだろうか。あら? page_lookup が微妙なのかな。なんかコメントにそれっぽい記述あり。

// Return the page mapped at virtual address 'va'.
// If pte_store is not zero, then we store in it the address
// of the pte for this page.  This is used by page_remove and
// can be used to verify page permissions for syscall arguments,
// but should not be used by most callers.
//
// Return NULL if there is no page mapped at va.
//
// Hint: the TA solution uses pgdir_walk and pa2page.

実装ですが、引数の pte_store は何も操作してません。なんかずるずるバグが出てくるこのカンジはある意味微妙ッス。