MBanks. Each memory bank contains a page
allocator and supports allocation and deallocation of page frames,
PFrames, contained in it. Also, it provides means to locate a page
frame either by distributed address or physical address.
<Off memory bank. >= (U->)
// A bank of memory
class off_MBank : public off_HWCompResource {
private:
off_PgAllocator m_alloc; // A page allocator.
<Other private members of off_MBank. >
<Other private methods of off_MBank. >
protected:
<Other protected methods of off_MBank. >
public:
// Returns a pointer to the memory bank page allocator.
virtual off_PgAllocator *get_allocator(void) {
assert(valid()); return &m_alloc;
}
<Other public methods of off_MBank. >
};
Definesoff_MBank(links are to index).
<Off memory bank dependencies. >= (U->) [D->] #include <flux/types.h> // for boolean_t natural_t et al. #include <klib/ids.h> // for off_pg_id_t et al. #include <klib/HWCompResource.h> // for off_HWCompResource #include <hw/PFrame.h> // for off_PFrame #include <hw/PgAllocator.h> // for off_PgAllocator
Being a concrete subclass of CompResource, we must implement
make_available.
<Other public methods ofoff_MBank. >= (<-U) [D->] <Implementation ofoff_CompResource::make_available. >
Such implementation uses is_local
<Other private methods of off_MBank. >= (<-U) [D->]
// Is this identifier a local one?
boolean_t is_local(const off_pg_id_t &pg) {
return ( pg.i_slot <= m_alloc.get_length() &&
(m_alloc+(natural_t)pg.i_slot)->l_pf.get_id() == pg );
}
Initially memory banks are created with default values for its members.
<Other public methods ofoff_MBank. >+= (<-U) [<-D->] // Creates a memory bank. off_MBank(void) : off_HWCompResource(off_Protection(),OFF_PRTL_NULL, OFF_ID_NULL, OFF_MAGIC_MBANK), <Initialize other aggregate members ofoff_MBank. > {;}
<Off memory bank dependencies. >+= (U->) [<-D->] #include <klib/prot.h> // for off_Protection #include <klib/Magic.h> // for magic numbers
We must define the new magic number used before.
<Off magic numbers. >= [D->] OFF_MAGIC_MBANK, // Memory bank magic nb.
We can now add another case to the implementation of
Magic::nameof.
<off_Magic::nameofcase form_numbers. >= [D->] case OFF_MAGIC_MBANK: return "off_MBank";
Memory bank users can check their references to memory banks using this method.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Does this look like a MBank?
boolean_t valid(void) const { return get_magic() == OFF_MAGIC_MBANK; }
As not every memory is of the same kind, memory banks export some information to let users know about the characteristics of the supplied memory. Namely, there are three boolean attributes to record whether the memory is DMA capable, IO-map capable, and persistent or not. We store these three bits in the unused part of the resource flag bits.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Supports DMA?
boolean_t is_dmable(void) const {
assert(valid()); return r_flags&OFF_MF_DMA;
}
// Supports IO port maps.
boolean_t is_iomapable(void) const {
assert(valid()); return r_flags&OFF_MF_IOM;
}
// Persists?
boolean_t is_persistent(void)const {
assert(valid()); return r_flags&OFF_MF_PER;
}
After instantiation, start should be used to start memory bank
operation.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Starts normal operation for this memory bank.
err_t start( const off_mdepMBank &mdep, const off_id_t &id );
<Off memory bank dependencies. >+= (U->) [<-D->] #include <hw/mdep/mMBank.h> // for off_mdepMBank #include <klib/err.h> // for err_t and error numbers.
Such method receives the boot-time information about the features for the memory bank being started and sets it up accordingly. It will also set the memory bank identifier as dictated by the caller4.1.
The information contained in mdep is the first valid address, the
last valid address, the capabilities for the memory being contained
(with bits MF_DMA if DMA is suported, MF_IOM if IO-map is
supported, MF_PER is the memory persists), the preferred page
sizes, and the mean access time.
<off_MBank::startimplementation. >= (U->) // Starts normal operation for this memory bank. err_t off_MBank::start( const off_mdepMBank &mdep, const off_id_t &id ) { assert(valid()); set_id(id); r_flags |= mdep.get_flags(); <Start other members ofoff_MBankaccording tomdep. > kcout<< "Mbank #"<< id << fmt(" (%d pages) ",get_npgs())<< "started." << nl; return EOK; }
A new member, m_mdep will store the boot time information, which
is needed because it provides common information useful to memory bank
users.
<Other private members of off_MBank. >= (<-U) [D->]
off_mdepMBank m_mdep; // Mach. dep. information
<Start other members ofoff_MBankaccording tomdep. >= (<-U) [D->] m_mdep=mdep;
Initially, before the memory bank is started it is initialized with silly values.
<Initialize other aggregate members of off_MBank. >= (<-U) [D->]
m_mdep(0),
Most of the machine dependent information is exported by the memory bank.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Returns the first valid address.
vm_offset_t get_first(void)const{assert(valid()); return m_mdep.get_first(); }
// Returns the last valid address.
vm_offset_t get_last(void)const{ assert(valid());return m_mdep.get_last(); }
// Returns the number of page frames.
vm_size_t get_npgs(void) const { assert(valid());return m_mdep.get_npgs();}
// Returns the mean access time.
// (0 means damn slow; 255 means damn fast)
unsigned8_t get_mat(void) const {assert(valid()); return m_mdep.get_mat(); }
<Off memory bank dependencies. >+= (U->) [<-D->] #include <assert.h> // for assert
The get_pgsize method just returns the appropriate value found in
m_pgsz.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Gets the preferred page sizes
// above llimit bytes (or 0 if none)
vm_size_t get_pgsize(vm_size_t llimit) const;
Its implementation is somewhat slow.
<off_MBank::get_pgsize implementation. >= (U->)
// Gets the preferred page sizes
// above llimit bytes (or 0 if none)
vm_size_t off_MBank::get_pgsize(vm_size_t llimit) const
{
assert(valid());
for (int i=0; i<OFF_NPGSZ_MAX; i++)
if (m_mdep.get_pgsz(i)>llimit)
return m_mdep.get_pgsz(i);
return 0;
}
And a more efficient get_pgsize routine is provided to be used
inside the kernel. It always returns the minimum preferred page size.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Returns the minimum preferred page size.
static vm_size_t get_pgsize(void) { return off_mdepMBank::get_pgsz();}
<Off memory bank implementation dependencies. >= (U->) [D->] #include <klib/limits.h> // for OFF_NPGSZ_MAX and other limits.
The maximum number of preferred page sizes is a system limit.
<Off limits. >= const int OFF_NPGSZ_MAX = 4; // Max. number of pages sizes.
DefinesOFF_NPGSZ_MAX(links are to index).
Each PFrame maintains the information needed about each page frame
installed in the system: some bits stating whether the page has been
referenced or not, is clean or dirty, and also that state kept by
every system hardware resource unit, including the magic number, a
reference to the container the page is in, the reference counter,
protection, domain, and resource id.
<Off page frame. >= (U->)
// A page frame.
//
class off_PFrame : public off_HWResUnit {
private:
off_pgf_t p_bits; // Status bits.
<Other private members of off_PFrame. >
protected:
<Other protected methods of off_PFrame. >
public:
// Gets the page frame bits.
off_pgf_t get_bits(void) const { assert(valid()); return p_bits; }
// Sets the page frame bits.
void set_bits(const off_pgf_t b) { assert(valid()); p_bits=b;}
<Other public methods of off_PFrame. >
};
Definesoff_PFrame(links are to index).
We have used valid, which will be defined later.
<Off page frame dependencies. >= (U->) [D->] #include <assert.h> // for assert #include <klib/HWResUnit.h> // for off_HWResUnit class off_MBank; // to break the circular dependency
where p_bits corresponds to the bits maintained by the address
translation machinery and defined in a machine dependent way.
<Off page frame mode bits. >= (U->) typedef off_mdep_pgf_t off_pgf_t;
<Off page frame dependencies. >+= (U->) [<-D->] #include <hw/mdep/mPFrame.h> // for off_mdep_pgf_t
Page frames are created with this constructor,
<Other public methods of off_PFrame. >= (<-U) [D->]
// Creates a pre-initialized page frame.
off_PFrame(const off_Protection &p, const off_prtl_id_t domain);
although a trivial constructor is provided so that uninitialized
PFrame structures be allowed.
<Other public methods of off_PFrame. >+= (<-U) [<-D->]
off_PFrame(void) :
off_HWResUnit(off_Protection(),
OFF_PRTL_NULL,
OFF_ID_NULL,
OFF_MAGIC_PFRAME,NULL)
{;}
<Off page frame dependencies. >+= (U->) [<-D->] #include <klib/prot.h> // for off_Protection #include <klib/ids.h> // for OFF_PRTL_NULL et al. #include <klib/Magic.h> // for magic numbers.
The real constructor only receives the protection and domain to be set for the page frame being allocated.
<off_PFrame::off_PFrame implementation. >= (U->)
off_PFrame::off_PFrame(const off_Protection &p, const off_prtl_id_t domain) :
off_HWResUnit(p,domain),
p_bits(0)
{;}
This means that page frames are pre-initialized. The identifier, container reference and magic number is set at initialization time by the page frame container.
We still have to define now the magic number for page frames.
<Off magic numbers. >+= [<-D] OFF_MAGIC_PFRAME, // Off page frame magic number
We can now add another case to the implementation of
Magic::nameof.
<off_Magic::nameofcase form_numbers. >+= [<-D] case OFF_MAGIC_PFRAME: return "off_PFrame";
Page frame users can check their references using this method.
<Other public methods of off_PFrame. >+= (<-U) [<-D->]
// Does this look like a PFrame?
boolean_t valid(void) const { return get_magic() == OFF_MAGIC_PFRAME; }
The new container can also implement its get_umagic method.
<Other protected methods of off_MBank. >= (<-U) [D->]
virtual off_magic_t get_umagic(void) { return OFF_MAGIC_PFRAME; }
\subsection{Memory allocation}
Page frames are allocated from PgAllocators. A subclass
of a FixedAllocator instantiated to allocate PFrames.
<Off page frame allocator. >= (U->)
// A page frame allocator.
//
class off_PgAllocator : public off_TFixedAllocator<off_LPFrame> {
private:
<Other private members of off_PgAllocator. >
public:
// Allows declarations of uninitialized page allocators
off_PgAllocator(void){;}
const off_PgAllocator &operator=(const off_PgAllocator &other) {
return (*this=other);
}
<Other public methods of off_PgAllocator. >
};
Definesoff_PgAllocator(links are to index).
As the fixed allocator uses a liked free-list, the array nodes must
subclass DLinkedNode.
<Off linkable page frame. >= (U->)
// A linkable off_PFrame
//
class off_LPFrame : public DLinkedNode {
public:
off_PFrame l_pf; // The page frame.
// To allow uninitilized LPFrames.
off_LPFrame(void) {;}
<Other public methods of off_LPFrame. >
};
Definesoff_LPFrame(links are to index).
<Off page frame allocator dependencies. >= (U->) [D->] #include <klib/FixedAllocator.h> // for off_FixedAllocator #include <hw/PFrame.h> // for off_PFrame
Page frame allocators (there is one per memory bank) share a single
array holding PFrames for the entire physical memory. This is
convenient to ensure that memory banks sharing a single node will
share the physical address space, which is the common case. For that
reason, a page allocator is able accept an initial page frame number
which will adjust every page frame number used on it.
<Other private members of off_PgAllocator. >= (<-U) [D->]
natural_t p_start; // first PFN
An array of linked page frames, as well as an indexer for it are
shared among PgAllocators.
<Other private members of off_PgAllocator. >+= (<-U) [<-D]
static off_Indexer<off_LPFrame> p_idx; // linked page frame indexer.
static off_LPFrame *p_pg; // Linked Page array.
Such array is staticly set to be null.
<Off page frame allocator static members. >= (U->) off_LPFrame *off_PgAllocator::p_pg=NULL; // Linked page arry. off_Indexer<off_LPFrame> off_PgAllocator::p_idx; // Linked page indexer.
<Off page frame allocator dependencies. >+= (U->) [<-D->] #include <klib/dlist.h> // for DLinkedNode et al #include <klib/Indexer.h> // for off_Indexer
It is later initialized (just once!) by the set_core method which
should be called before any off_PgAllocator instance be
created.
<Other public methods of off_PgAllocator. >= (<-U) [D->]
// Sets the off_PgAllocator idea of core to #npgs page frames.
static err_t set_core(natural_t npgs);
<off_PgAllocator::set_core implementation. >= (U->)
err_t off_PgAllocator::set_core(natural_t npgs)
{
if (!p_pg)
p_pg = new off_LPFrame[npgs];
return (p_pg) ? EOK : ENOMEM;
}
<Off page frame allocator dependencies. >+= (U->) [<-D->] #include <flux/types.h> // for boolean_t natural_t et al. #include <klib/err.h> // for err_t and error numbers
This method is re-exported by MBank.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Sets the idea of core to #npgs page frames.
static err_t set_core(const off_mdepMBank &mdep);
<off_MBank::set_core implementation. >= (U->)
// Sets the idea of core to #npgs page frames.
err_t off_MBank::set_core(const off_mdepMBank &mdep)
{
m_coresize=off_mdepMBank::p2a(mdep.get_core_npgs());
return off_PgAllocator::set_core(mdep.get_core_npgs());
}
Some allocator methods are redefined to take into account that the first PFN might be non-zero and also to improve efficiency.
<Other public methods of off_PgAllocator. >+= (<-U) [<-D->]
off_LPFrame *allocate(natural_t at, boolean_t use_revocation=FALSE) {
return off_TFixedAllocator<off_LPFrame>::allocate(at-p_start,
use_revocation);
}
inline off_LPFrame *operator +(natural_t at) {
return p_pg+at;
}
inline natural_t pos(off_LPFrame *p) {
return p-p_pg;
}
The size of the core is kept in a local member which is exported to the rest of the kernel.
<Other private members of off_MBank. >+= (<-U) [<-D->]
static vm_size_t m_coresize; // Size of core memory.
<Off memory bank static members. >= (U->) [D->] vm_size_t off_MBank::m_coresize=0; // Size of core memory.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Return the core size.
static inline vm_size_t get_coresize(void) { return m_coresize; }
When the page allocator is created we specify the starting page, the number of
page frames and the base address for the very first page frame. We
then fix the allocator raw store as an offset into the shared
n_pg array.
<Other public methods of off_PgAllocator. >+= (<-U) [<-D->]
// Creates a page allocator.
off_PgAllocator(off_Exhausted *r,vm_offset_t start,vm_size_t size);
<off_PgAllocator::off_PgAllocator implementation. >= (U->)
// Creates a page allocator.
off_PgAllocator::off_PgAllocator(off_Exhausted *r,
vm_offset_t start,vm_size_t size):
off_TFixedAllocator<off_LPFrame>(r,
(const off_Indexable*)&p_idx,
p_pg+start,size),
p_start(start)
{;}
<Off page frame allocator dependencies. >+= (U->) [<-D] #include <klib/Allocator.h>
<Off page frame allocator implementation dependencies. >= (U->) #include <hw/MBank.h>
The page allocator is initialized when the memory bank is started and
its size is known. As addresses contained in mdep refer to
bytes, we have to convert them to page numbers4.2 using the machine
dependent method a2p. Besides, the memory bank itself is given to
the allocator as a resource revocation notifier.
<Start other members ofoff_MBankaccording tomdep. >+= (<-U) [<-D->] (void) new((void*)&m_alloc) off_PgAllocator((off_Exhausted*)this, mdep.a2p(mdep.get_first()), mdep.a2p(mdep.get_last()-mdep.get_first()));
where we used a special new operator of PgAllocator to do
initialization.
<Other public methods of off_PgAllocator. >+= (<-U) [<-D]
void *operator new(size_t s, void *p) { (void)s; return p; }
The allocator is always indexed by page frame numbers, the memory bank by page frame physical addresses instead.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
//Gets a page frame from its distributed or physical address
off_PFrame *operator+(vm_offset_t ma) {
assert(valid());
return &(m_alloc+off_mdepMBank::a2p(ma))->l_pf;
}
The notify method must be provided by memory banks to implement
the resource revocator signature (note how this was passed by the
memory bank to the page allocator constructor).
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Notifies that memory is exhausted.
void notify(void);
Using such allocator, a memory bank can allocate memory for
PFrames.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Allocates a page frame.
off_PFrame *alloc(boolean_t use_revocation=FALSE);
off_PFrame *alloc(const off_pg_id_t &at, boolean_t use_revocation=FALSE);
// Deallocates a page frame.
void free(const off_PFrame *pf);
<off_MBank::allocandfreeimplementation. >= (U->) // Allocates a page frame. off_PFrame *off_MBank::alloc(boolean_t use_revocation) { off_LPFrame *lpf; assert(valid()); w_lock(); lpf=m_alloc.allocate(use_revocation); w_unlock(); return (lpf)?(&lpf->l_pf):(off_PFrame*)NULL; } off_PFrame *off_MBank::alloc(const off_pg_id_t &at, boolean_t use_revocation) { off_LPFrame *lpf; assert(valid()); w_lock(); lpf=m_alloc.allocate(a2p(at),use_revocation); w_unlock(); return (lpf)?(&lpf->l_pf):(off_PFrame*)NULL; } // Deallocates a page frame. void off_MBank::free(const off_PFrame *pf) { off_LPFrame *lpf=(off_LPFrame*)(((char*)pf)-(int)&off_LPFrame::l_pf); assert(valid() && pf); w_lock(); m_alloc.deallocate(lpf); w_unlock(); }
Page frames are allocated and deallocated with new and delete
operators.
The operator new receives the memory bank where the page frame
should be allocated. It uses its alloc method to find an available
page frame.
<Other public methods of off_PFrame. >+= (<-U) [<-D->]
// Allocate a page frame.
void *operator new(size_t s, off_MBank *mbank,
boolean_t use_revocation=FALSE);
void *operator new(size_t s, off_MBank *mbank,
const off_pg_id_t &at,
boolean_t use_revocation=FALSE);
<off_PFrame::operator new implementation. >= (U->)
void *off_PFrame::operator new(size_t s, off_MBank *mbank,
boolean_t use_revocation=FALSE)
{
off_PFrame *pf=mbank->alloc(use_revocation);
(void)s;
assert(s && mbank);
assert(!pf || pf->valid());
return (void*)pf;
}
void *off_PFrame::operator new(size_t s, off_MBank *mbank,
const off_pg_id_t &at,
boolean_t use_revocation=FALSE)
{
off_PFrame *pf=mbank->alloc(at,use_revocation);
(void)s;
assert(s && mbank && mbank->valid());
assert(!pf || pf->valid());
return (void*)pf;
}
The operator delete is also provided.
<Other public methods of off_PFrame. >+= (<-U) [<-D->]
// Deallocates this page frame.
void operator delete(void *p);
<off_PFrame::operator delete implementation. >= (U->)
// Deallocates this page frame.
void off_PFrame::operator delete(void *p)
{
off_PFrame *pf=(off_PFrame*)p;
assert(pf && pf->valid());
((off_MBank*)(pf->get_container()))->free(pf);
}
We said that page frames are preinitialized, but we have not preinitialized them yet. We only have to iterate on the page array and set the container reference and the page identifier. The magic number is set by the zero-argument page constructor.
<Start other members ofoff_MBankaccording tomdep. >+= (<-U) [<-D->] for (natural_t pg=mdep.a2p(mdep.get_first()); pg<mdep.a2p(mdep.get_last()); pg++) { off_pg_id_t pid(get_id(),mdep.p2a(pg)); (void) new(m_alloc+pg) off_LPFrame(pid,this); }
Note the trick, a new operator for LPFrame returning its
own argument is used,
<Other public methods of off_LPFrame. >= (<-U) [D->]
void *operator new(size_t s, off_LPFrame *p) { (void)s; return (void*) p; }
then a constructor will initialize those fields which can be pre-initialized.
<Other public methods of off_LPFrame. >+= (<-U) [<-D]
off_LPFrame(const off_pg_id_t &id,off_MBank *c) : l_pf(id,c) {;}
It calls in turn another pre-initialization constructor for
PFrames.
<Other public methods of off_PFrame. >+= (<-U) [<-D->]
off_PFrame(const off_pg_id_t &id,off_MBank *c) :
off_HWResUnit(id,OFF_MAGIC_PFRAME,(off_HWCompResource*)c)
{;}
\subsection{Memory revocation}
Each memory bank delivers revocation events to its exception portal or
r_domain, in case that portal has been set. The owner of that
portal is the referee for page revocation inside the memory bank.
<off_MBank::notify implementation. >= (U->)
// Notifies that memory is exhausted.
void off_MBank::notify(void)
{
assert(valid());
if (get_domain()==OFF_PRTL_NULL)
return;
else {
;
#if 0 // XXX fix this
off_ExhaustReq m(OFF_MAGIC_MBANK,get_id());
prtl.kpct(get_domain(),off_Shtl::self(),sizeof(m),0,&m,NULL,0);
#endif
}
}
<Off memory bank implementation dependencies. >+= (U->) [<-D->] #include <prtl/ex.h> // for off_ExhaustReq //#include <prtl/PrtlSrv.h> // for prtl. XXX fix this //#include <hw/Processor.h> // for Processor and self.
As you can see, no reply is actually expected. But we will not recover
control until the referee decided to return from the PCT. In that way
the user allocation which triggered resource revocation is likely to
be satisfied and there is no necessity of EAGAIN error codes.
The revocation message contains the magic number of the kind of container being exhausted (so that the referee could know which resource has been exhausted) and the identifier of the container.
<Off user-kernel messages. >=
// Exhausted message.
struct off_ExhaustReq : public off_MsgReq {
off_magic_t m_kind; // Resource kind
off_id_t m_id; // Container exhausted.
// Creates an exhausted request.
off_ExhaustReq(const off_magic_t kind, const off_id_t &id) :
off_MsgReq(OFF_EX_UNAVAILABLE), m_kind(kind), m_id(id) {;}
};
Definesoff_ExhaustReq(links are to index).
<Off user-kernel messages dependencies. >= #include <klib/Magic.h> // for off_magic_t and magic numbers.
\subsection{Memory navigation and inspection}