CS 134

Understanding VM Objects and vm_object_setsize in OS/161

Overview

VM Objects represent contiguous regions of virtual memory in OS/161, such as text segments, data segments, stacks, and heaps. Each VM object contains an array of logical pages (lpages) and manages their lifecycle. The vm_object_setsize function is crucial for implementing dynamic memory allocation via sbrk.

Key Data Structure

struct vm_object {
    struct lpage_array *vmo_lpages;  /* array of logical pages */
    vaddr_t vmo_base;                /* base virtual address */
    size_t vmo_lower_redzone;        /* size of protected area below */
};

Understanding vm_object_setsize

Purpose

vm_object_setsize changes the size of a VM object by modifying its lpage array, which is the core functionality needed for

  • Growing the heap when sbrk is called with a positive increment
  • Shrinking the heap when sbrk is called with a negative increment

Current Limitations

The provided implementation is a stub that

  • Returns success if new sizecurrent size
  • Returns ENOMEM if trying to grow
  • Doesn't actually modify the object

Key Requirements for Implementation

  1. Handle Both Growth and Shrinkage
    • When growing: allocate new zero-filled pages
    • When shrinking: clean up existing pages
  2. Manage Swap Space
    • Reserve space for new pages
    • Release space for removed pages
    • Use swap_reserve() and swap_unreserve()
  3. Handle Memory Cleanup
    • Clean up pages properly when shrinking
    • Use vm_object_zap as a reference
  4. Maintain TLB Consistency
    • Clear TLB entries for removed pages
    • Use mmu_unmap() when removing pages

Key Functions You'll Need

Array Management

/* Get number of pages in VM object */
unsigned lpage_array_num(struct lpage_array *array);

/* Get page at index */
struct lpage *lpage_array_get(struct lpage_array *array, unsigned index);

/* Set page at index */
void lpage_array_set(struct lpage_array *array, unsigned index,
                    struct lpage *page);

/* Change array size */
int lpage_array_setsize(struct lpage_array *array, unsigned newsize);

Memory Management

/* Reserve swap space */
int swap_reserve(unsigned long npages);
void swap_unreserve(unsigned long npages);

/* Clean up a page */
void lpage_destroy(struct lpage *lp);

/* Remove TLB entry */
void mmu_unmap(struct addrspace *as, vaddr_t va);

Implementation Strategy

Size Comparison

unsigned oldsize = lpage_array_num(vmo->vmo_lpages);
if (npages == oldsize) {
    return 0;  /* Nothing to do */
}

Shrinking Case

When npages < oldsize:

  • Clean up removed pages
  • Release swap space
  • Shrink array

Growing Case

When npages > oldsize:

  • Reserve swap space
  • Grow array
  • Initialize new entries

Common Pitfalls

  1. Resource Management
    • Leaking swap space
    • Not cleaning up on errors
    • Not maintaining swap reservations
  2. Error Handling
    • Not handling allocation failures
    • Not cleaning up on partial failures
    • Not maintaining consistency on errors
  3. TLB Management
    • Forgetting to clear TLB entries
    • Not calculating virtual addresses correctly

Debugging Tips

  1. Check Resource Usage
    • Monitor swap-space allocation
    • Watch for memory leaks
    • Verify page cleanup
  2. Test Cases to Consider
    • Grow by 1 page
    • Grow by many pages
    • Shrink by 1 page
    • Shrink by many pages
    • Grow then shrink
    • Shrink then grow
    • Grow with insufficient swap space

Example Usage Flow

Here's how sbrk uses vm_object_setsize:

/* In as_sbrk: */
newbreak = as->as_break + increment;
newvpage = ROUNDUP(newbreak, PAGE_SIZE);
newnpages = (newvpage - vmo->vmo_base) / PAGE_SIZE;

result = vm_object_setsize(as, vmo, newnpages);
if (result) {
    return result;
}

(When logged in, completion status appears here.)