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
sbrkis called with a positive increment - Shrinking the heap when
sbrkis called with a negative increment
Current Limitations
The provided implementation is a stub that
- Returns success if new size ≤ current size
- Returns
ENOMEMif trying to grow - Doesn't actually modify the object
Key Requirements for Implementation
- Handle Both Growth and Shrinkage
- When growing: allocate new zero-filled pages
- When shrinking: clean up existing pages
- Manage Swap Space
- Reserve space for new pages
- Release space for removed pages
- Use
swap_reserve()andswap_unreserve()
- Handle Memory Cleanup
- Clean up pages properly when shrinking
- Use
vm_object_zapas a reference
- 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
- Resource Management
- Leaking swap space
- Not cleaning up on errors
- Not maintaining swap reservations
- Error Handling
- Not handling allocation failures
- Not cleaning up on partial failures
- Not maintaining consistency on errors
- TLB Management
- Forgetting to clear TLB entries
- Not calculating virtual addresses correctly
Debugging Tips
- Check Resource Usage
- Monitor swap-space allocation
- Watch for memory leaks
- Verify page cleanup
- 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.)