next up previous contents
Next: 4.0.5 Memory banks for Up: 4. Exporting the hardware Previous: 4.0.3 Kernel pages, reserved

4.0.4 Multiple page size support

To support multiple page sizes we use multiple memory banks covering the same regions of memory. Page sizes are a multiple of the minimum page size, thus we maintain a primary memory bank with page frames of the minimum (primary) size. For each multiple page size we maintain a secondary memory bank aggregating as many primary pages as necessary into each secondary page. These secondary memory banks are not real ones, they only translate a single operation into multiple operations on primary pages.

<Off secondary memory bank. >=
class off_SMBank : public off_MBank {
public:
  // Creates a secondary memory bank for the given primary one. 
  inline off_SMBank(off_MBank *primary, natural_t shift_factor);

  <Other public methods of off_SMBank. >
protected:
  off_MBank *m_primary; // Primary memory bank.
  natural_t m_shift;    // sizeof(primary_g)<<m_shift==sizeof(secondary_pg).
};
Defines off_SMBank (links are to index).

When a secondary bank is created it initializes itself as any other memory bank and records the address of the primary bank.

<off_SMBank::off_SMBank implementation. >=
// Creates a secondary memory bank for the given primary one. 
inline off_SMBank::off_SMBank(off_MBank *primary, natural_t shift_factor) : 
  off_MBank(),
  m_primary(primary),
  m_shift(shift_factor)
{;}

As secondary banks do not have the minimum preferred page size they must redefine get_pgsize to return the minimum preferred size for this secondary bank (which is a multiple of that in the primary bank). Note that get_pgsize is for in-kernel use only.

<Other public methods of off_SMBank. >= (<-U) [D->]
// Returns the minimum preferred page size. 
vm_size_t get_pgsize(void);

<off_SMBank::get_pgsize implementation. >=
// Returns the minimum preferred page size. 
vm_size_t get_pgsize(void)
{
 return m_mdep.get_pgsz(0);
}

Page frame numbers have now a different meaning, thus we have to redefine more methods.

<Other public methods of off_SMBank. >+= (<-U) [<-D->]
//Gets a page frame from its distributed or physical address
off_PFrame *operator+(vm_offset_t ma);
// Address to PFN.
vm_offset_t a2p(vm_offset_t va);
// PFN to address. 
vm_offset_t p2a(vm_offset_t va);
// Returns the mask for page offsets.
vm_offset_t pmask(void);

<off_SMBank::operator+,a2p,p2a and pmask implementation. >=
//Gets a page frame from its distributed or physical address
off_PFrame *off_SMBank::operator+(vm_offset_t ma) {
    assert(valid());
    return &(m_alloc+a2p(ma))->l_pf;
}

// Address to PFN.
vm_offset_t off_SMBank::a2p(const vm_offset_t va)
{
  return m_mdep.a2p(va); 
}

// PFN to address. 
vm_offset_t off_SMBank::p2a(vm_offset_t va)
{
  return m_mdep::p2a(va); 
}

// Returns the mask for page offsets.
vm_offset_t off_SMBank::pmask(void)
{
  return m_mdep::pmask(); 
}

Besides, we must maintain the coherency between the primary and the secondary banks. That means that a secondary page must be considered to be allocated when any of its primary pages are allocated, and the same for deallocation, protection, etc. We do such synchronization lazily.

When a secondary page is allocated, we double-check that all its primary pages are available and allocate them. Should allocation of primary pages fail, we consider the secondary page to be allocated and retry the operation. In that case, the secondary page is protected with the kernel domain and access rights.

<Other public methods of off_SMBank. >+= (<-U) [<-D]
// Allocates a page frame. 
off_PFrame *alloc(boolean_t use_revocation);
off_PFrame *alloc(const off_pg_id_t &at, boolean_t use_revocation);
// Deallocates a page frame. 
void free(const off_PFrame *pf);

When a secondary page is deallocated we deallocate all of its primary pages.

<off_SMBank::alloc implementation. >=
// Allocates a page frame. 
off_PFrame *off_SMBank::alloc(boolean_t use_revocation)
{
  off_PFrame *attempt;
  vm_offset_t m;
  natural_t count;

  while ((attempt=off_MBank::alloc(use_revocation))!= NULL)
    if (attempt->alloc_primaries())
      return attempt;
    
  // This is what alloc_primaries should do. 
    for(count=1<<m_shift,m=*attempt; 
        count && m_primary->alloc(m,use_revocation);
        count--,m+=m_primary->get_pgsize())
      ;
    if (count)
      while(count--){
        m_primary->free(*m_primary+m);
        m-=m_primary->get_pgsize();
      }
    else 
      return attempt;
  }
  return NULL;
}

off_PFrame *off_SMBank::alloc(const off_pg_id_t &at, boolean_t use_revocation);
// Deallocates a page frame. 
void off_SMBank::free(const off_PFrame *pf);

Secondary page operations behave as expected; besides, each page operation is forwarded to all involved primary pages.

NOTE: Will need to redefined free to free the primary pages, and also redefine every page operation to forward it to every primary page frame. A private iterator over primary pages will be handy. }

<#2727#>NOTE: Will need to redefined free to free the primary pages, and also redefine every page operation to forward it to every primary page frame. A private iterator over primary pages will be handy. }


next up previous contents
Next: 4.0.5 Memory banks for Up: 4. Exporting the hardware Previous: 4.0.3 Kernel pages, reserved
Francisco J. Ballesteros
1998-05-25