xv6, line by line
test yourself

Test yourself · category 14 of 20

Physical memory, sbrk and page faults

The kalloc free list and kmem.lock, junk fills and who zeroes pages, eager sbrk versus lazy sbrklazy, and how vmfault turns a page fault into a page or a kill.

1warm-upChoose one

kalloc and kfree keep a linked list of free pages. Where are the list’s nodes stored?

kernel/kalloc.c
17struct run {
18 struct run *next;
19};
21struct {
22 struct spinlock lock;
23 struct run *freelist;
4solidChoose one

kalloc fills each page it hands out with 0x05 bytes instead of zeros, even though many callers then zero it themselves. Why?

kernel/kalloc.c
68void *
69kalloc(void)
71 struct run *r;
75 if (r)
79 if (r)
80 memset((char *)r, 5, PGSIZE); // fill with junk
81 return (void *)r;
5warm-upClick the line

A program calls sbrklazy(4096). Click the line in sys_sbrk that is the entire allocation work the kernel does for it.

kernel/sysproc.c
43 int t;
44 int n;
46 argint(0, &n);
47 argint(1, &t);
50 if (t == SBRK_EAGER || n < 0) {
51 if (growproc(n) < 0) {
52 return -1;
53 }
54 } else {
55 // Lazily allocate memory for this process: increase its memory
56 // size but don't allocate memory. If the processes uses the
57 // memory, vmfault() will allocate it.
58 if (addr + n < addr)
59 return -1;
60 if (addr + n > TRAPFRAME)
61 return -1;
62 myproc()->sz += n;
63 }
64 return addr;

Your pick: none yet (click a line in the code)

6solidChoose all that apply

kalloc returns pages full of 0x05 junk. Which of these functions zero the page they get from kalloc before using it?

7solidClick the line

Click the line in vmfault that makes a user-mode store to the stack’s guard page fatal instead of quietly supplying a new page.

kernel/vm.c
463 if (va >= psz)
464 return 0;
467 return 0;
468 }
470 if (mem == 0)
471 return 0;
472 memset((void *)mem, 0, PGSIZE);
474 kfree((void *)mem);
475 return 0;
476 }
477 return mem;

Your pick: none yet (click a line in the code)

8solidPut in order

After sbrklazy(1 << 30), usertests stores to 0x13000, a page that is not mapped yet. Put the events in order.

  1. usertrap sees scause 15 and calls vmfault with p->sz and stval
  2. The store finds a level-0 PTE with V clear and raises a store page fault: scause 15, stval 0x13000
  3. userret installs the user page table between two sfence.vma and executes sret
  4. vmfault checks 0x13000 < sz and that the page is not mapped, then kallocs, zeroes and maps it R W U
  5. uservec saves the user registers in the trapframe and switches satp to the kernel page table
  6. The same store executes again, since sepc was not advanced, and succeeds
9deepFill in the machine state

A process takes a page fault on a lazily allocated heap page. usertrap calls vmfault, which calls kalloc, which has just acquired kmem.lock. What is the state of this hart?

kernel/trap.c
54 if (r_scause() == 8) {
55 // system call
57 if (killed(p))
58 kexit(-1);
60 // sepc points to the ecall instruction,
61 // but we want to return to the next instruction.
62 p->trapframe->epc += 4;
64 // an interrupt will change sepc, scause, and sstatus,
65 // so enable only now that we're done with those registers.
69 } else if ((which_dev = devintr()) != 0) {
70 // ok
71 } else if ((r_scause() == 15 || r_scause() == 13) &&
73 (r_scause() == 13) ? 1 : 0) != 0) {
74 // page fault on lazily-allocated page
10deepTrue or false, and why

True or false: after sbrk(-8192) frees two pages, xv6 must flush them from the TLB, but it forgets to, so the program could still reach the freed pages through stale TLB entries.

Why?

11deepType a number

In our run, the usertests child running lazy_alloc had p->sz = 0x12000 when it called sbrklazy(1 << 30). It then stores to 0x13000, 0x53000, … , every 64 pages, 4096 stores in all. Besides the 4096 data pages, how many page-table pages does walk allocate during these faults?

user/usertests.c
2627void
2630 char *i, *prev_end, *new_end;
2633 if (prev_end == (char *)SBRK_ERROR) {
2634 printf("sbrklazy() failed\n");
2639 for (i = prev_end + PGSIZE; i < new_end; i += 64 * PGSIZE)
2640 *(char **)i = i;
2642 for (i = prev_end + PGSIZE; i < new_end; i += 64 * PGSIZE) {
2643 if (*(char **)i != i) {
2644 printf("failed to read value from memory\n");
decimal, 0x hex or 0b binary
12warm-upChoose one

A process with p->sz = 0x5000 calls sbrk(65536) and it succeeds. What does sbrk return?

kernel/sysproc.c
43 int t;
44 int n;
46 argint(0, &n);
47 argint(1, &t);
50 if (t == SBRK_EAGER || n < 0) {
51 if (growproc(n) < 0) {
52 return -1;
53 }
54 } else {
55 // Lazily allocate memory for this process: increase its memory
56 // size but don't allocate memory. If the processes uses the
57 // memory, vmfault() will allocate it.
58 if (addr + n < addr)
59 return -1;
60 if (addr + n > TRAPFRAME)
61 return -1;
62 myproc()->sz += n;
63 }
64 return addr;
13solidChoose one

kexec maps the user stack’s guard page and then clears its PTE_U, instead of simply leaving the page unmapped. In this kernel, why does that difference matter?

kernel/exec.c
86 // Allocate some pages at the next page boundary.
87 // Make the first inaccessible as a stack guard.
88 // Use the rest as the user stack.
91 if ((sz1 = uvmalloc(pagetable, sz, sz + (USERSTACK + 1) * PGSIZE, PTE_W)) ==
92 0)
93 goto bad;
94 sz = sz1;
96 sp = sz;
14deepType a number

A process has p->sz = 0x5000 (page-aligned, nothing mapped above it). It calls sbrk(1) and then sbrk(1) again, both eager. How many physical pages does uvmalloc allocate in total across the two calls?

kernel/vm.c
215// Allocate PTEs and physical memory to grow a process from oldsz to
216// newsz, which need not be page aligned. Returns new size or 0 on error.
220 char *mem;
223 if (newsz < oldsz)
224 return oldsz;
227 for (a = oldsz; a < newsz; a += PGSIZE) {
229 if (mem == 0) {
231 return 0;
232 }
235 0) {
238 return 0;
239 }
240 }
241 return newsz;
decimal, 0x hex or 0b binary
15deepChoose one

A process has p->sz = 0x5000 (page-aligned, nothing mapped above it). It calls sbrklazy(1), then, as its very next memory access above 0x5000, stores a byte at 0x5800. What happens?

kernel/vm.c
17solidChoose one

A program grows its memory with sbrklazy, writes machine code into the new region (which faults the page in), and then jumps to it. What happens at the jump?

kernel/trap.c
69 } else if ((which_dev = devintr()) != 0) {
70 // ok
71 } else if ((r_scause() == 15 || r_scause() == 13) &&
73 (r_scause() == 13) ? 1 : 0) != 0) {
74 // page fault on lazily-allocated page
75 } else {
76 printk("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
77 printk(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
79 }
18warm-upTrue or false, and why

True or false: kalloc returns a page filled with zeros.

kernel/kalloc.c
68void *
69kalloc(void)
71 struct run *r;
75 if (r)
79 if (r)
80 memset((char *)r, 5, PGSIZE); // fill with junk
81 return (void *)r;

Why?

19solidMatch the pairs

Match each function with what it writes into a physical page it has just freed or obtained.

20deepChoose one

Imagine kmem.lock were removed. Hart 1 in kalloc reads the head (A); then hart 2 runs a complete kfree(P); then hart 1 stores A->next (B) as the new head. What is the result?

kernel/kalloc.c
57 r = (struct run *)pa;
65// Allocate one 4096-byte page of physical memory.
66// Returns a pointer that the kernel can use.
67// Returns 0 if the memory cannot be allocated.
68void *
69kalloc(void)
71 struct run *r;
75 if (r)
79 if (r)
80 memset((char *)r, 5, PGSIZE); // fill with junk
81 return (void *)r;
21solidChoose all that apply

Which of these calls make kfree panic? (In this build end = 0x80020bb0 and PHYSTOP = 0x88000000.)

kernel/kalloc.c
46void
47kfree(void *pa)
49 struct run *r;
51 if (((uint64)pa % PGSIZE) != 0 || (char *)pa < end || (uint64)pa >= PHYSTOP)
52 panic("kfree");
22warm-upChoose one

A program calls sbrklazy(-8192) to give back two pages. What does sys_sbrk do?

kernel/sysproc.c
43 int t;
44 int n;
46 argint(0, &n);
47 argint(1, &t);
50 if (t == SBRK_EAGER || n < 0) {
51 if (growproc(n) < 0) {
52 return -1;
53 }
54 } else {
55 // Lazily allocate memory for this process: increase its memory
56 // size but don't allocate memory. If the processes uses the
57 // memory, vmfault() will allocate it.
58 if (addr + n < addr)
59 return -1;
60 if (addr + n > TRAPFRAME)
61 return -1;
62 myproc()->sz += n;
63 }
64 return addr;
23solidType a number

The shell’s child has p->sz = 0x5000 and its whole memory is mapped through a single level-0 page-table page. malloc calls sbrk(65536) (eager). How many times is kalloc called during that system call?

kernel/vm.c
215// Allocate PTEs and physical memory to grow a process from oldsz to
216// newsz, which need not be page aligned. Returns new size or 0 on error.
220 char *mem;
223 if (newsz < oldsz)
224 return oldsz;
227 for (a = oldsz; a < newsz; a += PGSIZE) {
229 if (mem == 0) {
231 return 0;
232 }
235 0) {
238 return 0;
239 }
240 }
241 return newsz;
decimal, 0x hex or 0b binary
24warm-upChoose one

Physical memory is nearly exhausted. How does running out show up to a program that grew with eager sbrk, compared with one that used sbrklazy?

25warm-upDecode the bits

Right after exec, the level-0 PTE for sh’s page at 0x3000 (just below its one stack page) read 0x21fce407 in our run. Decode it.

Value: 0x21fce407