CS 134

Understanding Logical Pages (lpages) in OS/161

Overview

Logical pages (lpages) represent virtual pages in OS/161's VM system. Each lpage tracks both the physical-memory state (if the page is in RAM) and swap state (if the page is on disk) for a single virtual page. When implementing VM operations that manipulate pages, you'll need to understand how to properly create, manage, and destroy lpages.

Key Data Structure

struct lpage {
        volatile paddr_t lp_paddr;    /* physical addr (& flags in low bits) */
        off_t lp_swapaddr;            /* location in swap file */
        struct spinlock lp_spinlock;  /* synchronization */
}

Important aspects:

  • lp_paddr: Physical address where page resides (if in memory)
    • Low bits used for flags (like dirty state)
    • INVALID_PADDR if page is not in memory
  • lp_swapaddr: Where page is stored in swap
    • INVALID_SWAPADDR if no swap allocated
  • Protected by its own spinlock for thread safety

The macro LP_ISDIRTY(lp) checks whether the page is dirty (modified). The LP_SET and LP_CLEAR macros can be used to set or clear flags in the lp_paddr field (e.g., LP_SET(lp, LPF_DIRTY)).

struct lpage_array is a dynamic array of lpage structures, used to manage multiple pages together. It follows the interface described in array.h:

  • lpage_array_create — allocate an array of lpages
  • lpage_array_destroy — destroy an allocated array
  • lpage_array_init — initialize an array in externally allocated space
  • lpage_array_cleanup — clean up an array in externally allocated space
  • lpage_array_num — return number of elements in array
  • lpage_array_get — return element at index INDEX
  • lpage_array_set — set element at index INDEX to VAL
  • lpage_array_preallocate — allocate space without changing size
  • lpage_array_setsize — change size to NUM elements
  • lpage_array_add — append VAL to end of array
  • lpage_array_remove — remove entry at index INDEX and slide subsequent entries down

Key Functions for VM Operations

Creation and Destruction

/* Create a new lpage structure */
struct lpage *lpage_create(void);

/* Destroy an lpage and free its resources */
void lpage_destroy(struct lpage *lp);

Important notes:

  • lpage_create allocates the structure but doesn't allocate physical memory
  • lpage_destroy handles both memory and swap cleanup
  • Always check return value of lpage_create (might return NULL)

Page-State Management

/* Lock an lpage for exclusive access */
void lpage_lock(struct lpage *lp);

/* Release the lock */
void lpage_unlock(struct lpage *lp);

/* Lock the lpage and pin its physical page (if any) */
void lpage_lock_and_pin(struct lpage *lp);

Special Operations

/* Create a zero-filled page */
int lpage_zerofill(struct lpage **lpret);

/* Copy contents from one page to another */
int lpage_copy(struct lpage *from, struct lpage **toret);

Both of these functions use the internal helper lpage_materialize to create a new page and allocate swap and RAM for it. It doesn't initialize the page contents, but returns the page locked and the physical page pinned ready to be initialized by the caller.

Handling Faults and Evictions

/* Handle a page fault */
int lpage_fault(struct lpage *lp, struct addrspace *,
                int faulttype, vaddr_t va);

/* Evict a page from memory */
void lpage_evict(struct lpage *victim);

Common Usage Patterns

Cleaning Up a Page

/* Example of proper lpage cleanup */
if (lp != NULL) {
    /* Remove any TLB entries first */
    mmu_unmap(as, page_addr);
    /* Then destroy the page */
    lpage_destroy(lp);
}

Zero-Filled Pages

When an entry in the lpage array is NULL it means the page is zero-filled. This is a special state that is not in memory or swap, but promised to the user program (and swap has been reserved for it).

When a user program actually accesses a zero-filled page, the VM system must actually instantiate a new page and zero-fill it.

Instantiating a New Zero-Filled Page

When user code accesses a zero-filled page, the VM system must allocate a new page and zero-fill it. This is done using lpage_zerofill when a page fault occurs on a zero-filled page.

Proper Locking

/* Example of correct locking pattern */
lpage_lock(lp);
/* Do operations... */
lpage_unlock(lp);

Important Considerations

Resource Management

  • Always free both memory and swap space
  • Handle cleanup in error cases
  • Check return values from allocation functions

Synchronization

  • Use proper locking to prevent races
  • Follow lock ordering rules:
    • Coremap locks before lpage locks
    • Never hold multiple lpage locks
    • Be careful about lock/unlock ordering

Memory States

  • Pages can be:
    • In memory (valid physical address)
    • In swap (valid swap address)
    • Zero-filled (neither; will be zeroed on demand)
  • Track state changes correctly

Common Pitfalls

  1. Resource Leaks
    • Forgetting to free swap space
    • Not cleaning up on error paths
    • Missing lpage_destroy calls
  2. Synchronization Issues
    • Missing locks
    • Wrong lock order
    • Double locking
    • Not unlocking on all paths
  3. State Management
    • Not handling invalid states
    • Assuming pages are in memory
    • Not checking return values

Debugging Tips

  1. Watch for these symptoms:
    • Memory leaks
    • Page-content corruption
    • Deadlocks
    • System hangs
  2. Common error cases:
    • NULL pointer dereferences
    • Invalid page states
    • Lock/unlock mismatches

When Implementing VM Operations

Remember to

  1. Track all resources that need cleanup
  2. Handle all error cases gracefully
  3. Maintain proper synchronization
  4. Check return values
  5. Clean up in reverse order of allocation

Example Operation Flow

/*
 * Template for operations that manipulate lpages
 */
int some_operation(struct lpage *lp)
{
    int result;

    /* Lock the page */
    lpage_lock(lp);

    /* Verify state is valid */
    if ([invalid condition]) {
        lpage_unlock(lp);
        return ERROR;
    }

    /* Perform operation */
    result = do_something();
    if (result) {
        lpage_unlock(lp);
        return result;
    }

    /* Update state */
    update_state();

    /* Unlock */
    lpage_unlock(lp);
    return 0;
}

Remember: The lpage subsystem handles the details of page-content management. Your code should focus on proper resource management and maintaining consistency while using these facilities.

(When logged in, completion status appears here.)