xv6 lab copy-on-write lab: test copyinstr2 failed

110 Views Asked by At

when i work on the xv6 copy-on-write lab, i find that the copyinstr2 test is failed,here is the message and code: Message: $ usertests copyinstr2 usertests starting test copyinstr2: exec(echo, BIG) succeeded, should have failed FAILED SOME TESTS FAILED Code:

Add:

#referance count struct
struct {
  struct spinlock lock;
  uint count[38000];
} kreferance;

#referance functions
uint
krefget(uint64 pa)
{
  int i;
  uint ct;

  if(pa >= MAXVA)
    return -1;
  i = (PGROUNDDOWN(pa) - (uint64)end) / PGSIZE;
  acquire(&kreferance.lock);
  ct = kreferance.count[i];
  release(&kreferance.lock);

  return ct;
}
void
krefset(uint64 pa, uint ct)
{
  int i;

  if(pa >= MAXVA)
    panic("krefset");
  i = (PGROUNDDOWN(pa) - (uint64)end) / PGSIZE;
  acquire(&kreferance.lock);
  kreferance.count[i] = ct;
  release(&kreferance.lock);
}

#new pte flag
#define PTE_C (1L << 54) //copy on write flag
#define PTE_N (1L << 55) //no write flag

#copyonwrite()
int
copyonwrite(pagetable_t pagetable, uint64 va, pte_t *pte)
{
  uint64 pa;
  uint ct;

  pa= PTE2PA(*pte);

  if((ct = krefget(pa)) > 1){
    uint64 flags;
    void *mem;

    //alloc page
    if((mem = kalloc()) == 0)
      return -1;
    //copy content
    memmove(mem, (void *)pa, PGSIZE);
    //get flags
    flags = PTE_FLAGS(*pte);
    if((*pte & PTE_N) == 0){
      flags |= PTE_W;
    }
    //map new page
    *pte &= ~PTE_V;
    if(mappages(pagetable, va, PGSIZE, (uint64)mem, flags) != 0){
      kfree(mem);
      return -1;
    }
    //sub referance count
    krefset(pa, ct - 1);
  } else {
    //rm PTE_C flag
    *pte &= ~PTE_C;
    //recover PTE_W flag
    if((*pte & PTE_N) == 0){
      *pte |= PTE_W;
    } else {
      *pte &= ~PTE_N;
    }
  }
  return 0;
}

Change:
#change mappages() def for high bits pte flags
#PTE2PA()
#define PTE2PA(pte) ((((pte) & ~(0x7FL << 54)) >> 10) << 12)
#kinit()
void
kinit()
{
  initlock(&kmem.lock, "kmem");
+  initlock(&kreferance.lock, "kreferance");
+  memset((void *)kreferance.count, 0, 38000 * 4);
  freerange(end, (void*)PHYSTOP);
}

#kalloc()
void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r)
    kmem.freelist = r->next;
  release(&kmem.lock);
  
+  if(r)
+   krefset((uint64)r, 1);
  if(r)
    memset((char*)r, 5, PGSIZE); // fill with junk
  return (void*)r;
}
#kfree()
void
kfree(void *pa)
{
  struct run *r;
  uint ct;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

+  ct = krefget((uint64)pa);
+  if(ct <= 1){
+   // Fill with junk to catch dangling refs.
+    memset(pa, 1, PGSIZE);
+
+    r = (struct run*)pa;
+
+    acquire(&kmem.lock);
+    r->next = kmem.freelist;
+    kmem.freelist = r;
+    release(&kmem.lock);  
+  }else{
+    krefset((uint64)pa, ct - 1);
+  }
}
#uvmcopy()
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i, flags;

  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walk(old, i, 0)) == 0)
      panic("uvmcopy: pte should exist");
    if((*pte & PTE_V) == 0)
      panic("uvmcopy: page not present");
    //get physical address
+    pa = PTE2PA(*pte);
+    //get flags
+    flags = PTE_FLAGS(*pte);
+    //add flags like PTE_C, PTE_N
+    if(*pte & PTE_W){
+      flags &= ~PTE_W;
+      flags |= PTE_C;
+    } else {
+      flags |= PTE_N;
+      flags |= PTE_C;
+    }
+    //map page
+    if(mappages(new, i, PGSIZE, pa, flags) != 0)
+      goto err;
+    //change parent pagetable entry
+    if(*pte & PTE_W){
+      *pte &= ~PTE_W;
+      *pte |= PTE_C;
+    } else {
+      *pte |= PTE_N;
+      *pte |= PTE_C;
+    }
+    //add referance count for this page
+    krefset(pa, krefget(pa) + 1);
  }
  return 0;

 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  return -1;
}
#copyout()
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;
  pte_t *pte;

  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    //get pagetable entry
+    if((pte = walk(pagetable, va0, 0)) == 0)
+      return -1;
+    //if is a copy on write page then alloc new page
+    if(*pte & PTE_C){
+      if(copyonwrite(pagetable, va0, pte) != 0)
+        return -1;
+      //get new physical address
+      pa0 = walkaddr(pagetable, va0);
+    }
    n = PGSIZE - (dstva - va0);
    if(n > len)
      n = len;
    memmove((void *)(pa0 + (dstva - va0)), src, n);

    len -= n;
    src += n;
    dstva = va0 + PGSIZE;
  }
  return 0;
}
#usertrap()
void
usertrap(void)
{
  int which_dev = 0;

  if((r_sstatus() & SSTATUS_SPP) != 0)
    panic("usertrap: not from user mode");

  // send interrupts and exceptions to kerneltrap(),
  // since we're now in the kernel.
  w_stvec((uint64)kernelvec);

  struct proc *p = myproc();
  
  // save user program counter.
  p->trapframe->epc = r_sepc();
  
  if(r_scause() == 8){
    // system call

    if(killed(p))
      exit(-1);

    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    p->trapframe->epc += 4;

    // an interrupt will change sepc, scause, and sstatus,
    // so enable only now that we're done with those registers.
    intr_on();

    syscall();
  } else if((which_dev = devintr()) != 0){
    // ok
  //if is pagefault then deal with it
+  } else if(r_scause() == 0xc || r_scause() == 0xd || r_scause() == 0xf){
+    uint64 va;
+    pte_t *pte;
+   
+    //get virtual address
+    va = r_stval();
+    va = PGROUNDDOWN(va);
+    //get pagetable entry
+    if((pte = walk(p->pagetable, va, 0)) == 0)
+      p->killed = 1;
+    //if is copy on write then alloc new page
+    if(*pte & PTE_C){
+      if(copyonwrite(p->pagetable, va, pte) != 0)
+        p->killed = 1;
+    //if is other reason then kill proc
+    } else {
+      p->killed = 1;
+    }
  } else {
    printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
    printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
    setkilled(p);
  }

  if(killed(p))
    exit(-1);

  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2)
    yield();

  usertrapret();
}

after debugging i find that the fetchstr() in sys_exec() not retrun -1 and then it call exec() and run echo so that child return not 747 but -1, here is the wrong part of copyinstr2 test's code:

  if(pid == 0){
    static char big[PGSIZE+1];
    for(int i = 0; i < PGSIZE; i++)
      big[i] = 'x';
    big[PGSIZE] = '\0';
    char *args2[] = { big, big, big, 0 };
    ret = exec("echo", args2);
    if(ret != -1){
      printf("exec(echo, BIG) returned %d, not -1\n", fd);
      exit(1);
    }
    exit(747); // OK
  }

  int st = 0;
  wait(&st);
  if(st != 747){
    printf("exec(echo, BIG) succeeded, should have failed\n");
    exit(1);
  }

how can i fix it?

2023-10-16: i find that a page has PTE_W flag is copied as a no PTE_W flag page ,because the pte of parent is also copied from other and does not be copied to new page so it does not have PTE_W flag but PTE_C flag, i fix it by add new condition in uvmcopy, but is lose some free pages after usertests

    //add flags like PTE_C, PTE_N
if((*pte & PTE_W) == PTE_W){
  flags &= ~PTE_W;
  flags |= PTE_C;
} else if ((*pte & PTE_C) == 0 
    || (*pte & PTE_N) == PTE_N){
  flags |= PTE_N;
  flags |= PTE_C;
} else {
  flags |= PTE_C;
}
//map page
if(mappages(new, i, PGSIZE, pa, flags) != 0)
  goto err;
//change parent pagetable entry
if((*pte & PTE_C) == 0){
  if((*pte & PTE_W) == PTE_W){
    *pte &= ~PTE_W;
    *pte |= PTE_C;
  } else {
    *pte |= PTE_N;
    *pte |= PTE_C;
  }
}
0

There are 0 best solutions below