. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
off_u2.1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Resource2.2
off_ we will omit it in this text.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
_prot2.6
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
_prot2.5
<Off private members for protected objects. >= (U->) off_Protection _prot; // protection for this resource
Such member should be initialized with a user given protection p
at resource instantiation time.
<Off initialization for p protected objects. >= (U-> U->)
_prot=p;
<Off public methods for protected objects. >= (U->)
// Returns the protection.
const off_Protection &get_protection(void) const { return _prot; }
// Checks access for access rights R and operation OP.
inline boolean_t access_granted( const off_op_t op, const off_Rights &r ) {
return off_AccessChecker::access_granted(op,r,_prot);
}
// Sets P as protection for M access mode.
inline void protect(const off_Protection &p, const off_mode_t m=OFF_M_ALL) {
off_AccessChecker::protect(_prot,p,m);
}
The second one is exported to system users.
<Other public methods of off_uResource. >= (U->) [D->]
// Sets P as protection for M access mode.
void protect(const off_Protection &p, const off_mode_t m, const off_Rights &r);
The specific Protection mechanism being used can be changed
without disturbing the rest of the code. Once the best
protection mechanism be known empirically, the system can be optimized
by hardwiring it and avoiding dynamic dispatching.
Initially, a Protection is defined as a big random number per
operation. A special value is defined to grant access without further
checking. A Rights object is also a big random number. They are
used as capabilities. The AccessChecker considers the access to
be granted if Rights matches the numbers found in Protection
for the access mode being specified. We believe that any other
protection model can be quickly incorporated.
<Off protection. >= (U->)
// Random nb. used to protect operations.
typedef unsigned32_t off_cap_t;
// When set, grants access without further checking.
const off_cap_t OFF_CAP_ALLOW=0;
// Protection information for system resources.
//
class off_Protection {
public:
off_cap_t cap[OFF_NOPS]; // One random number per operation.
// Initializes a protection object.
// By default access is granted.
off_Protection(void){
for (natural_t i=0; i <OFF_NOPS; i++)
cap[i] = OFF_CAP_ALLOW;
}
<Other public methods of off_Protection. >
};
Definesoff_Protection(links are to index).
There is another constructor to set the same protection for every
access mode. Besides, it makes possible implicit conversion from
off_cap_t to Protection.
<Other public methods of off_Protection. >= (<-U) [D->]
// Initializes a protection object s.t. cap is used for every
// access mode.
off_Protection(const off_cap_t a_cap){
for (natural_t i=0; i <OFF_NOPS; i++)
cap[i] = a_cap;
}
This method generates a random protection.
<Other public methods of off_Protection. >+= (<-U) [<-D]
// Randomize this.
void randomize(void);
<off_Protection::randomize implementation. >= (U->)
// Randomize this.
void off_Protection::randomize(void)
{
off_cap_t a_cap=(off_cap_t)rand();
for (natural_t i=0; i <OFF_NOPS; i++)
cap[i] = a_cap;
}
<Off access rights. >= (U->)
// Access rights for system users.
//
class off_Rights {
public:
off_cap_t cap;
};
Definesoff_Rights(links are to index).
The implementation of AccessChecker methods is thus trivial if we
assume that valid access modes match with valid operations (i.e. an
access mode cannot contain most than one operation).
<off_AccessChecker::access_granted implementation. >= (U->)
// Checks access for access rights R and operation OP.
inline
boolean_t off_AccessChecker::access_granted( const off_op_t op,
const off_Rights &r ,
const off_Protection &p )
{
assert(off_op_valid(op));
return (r.cap == p.cap[op]);
}
Note how we used assert to ensure that op is valid. We will be
using it through the kernel code. Assertions are checked when
NDEBUG is not defined. We will also use kassert for a few
critical assertions which should be checked even when NDEBUG has
been defined.
<off_AccessChecker::protect implementation. >= (U->)
// Changes protection.
void off_AccessChecker::protect(off_Protection &old,
const off_Protection &now, const off_mode_t m)
{
assert(off_mode_valid(m));
if (OFF_M_R&m)
old.cap[OFF_OP_R] = now.cap[OFF_OP_R];
if (OFF_M_W&m)
old.cap[OFF_OP_W] = now.cap[OFF_OP_W];
if (OFF_M_X&m)
old.cap[OFF_OP_X] = now.cap[OFF_OP_X];
if (OFF_M_D&m)
old.cap[OFF_OP_D] = now.cap[OFF_OP_D];
if (OFF_M_P&m)
old.cap[OFF_OP_P] = now.cap[OFF_OP_P];
}
Finally, it should be clear that every system object exporting a
subset of methods to the user is responsible of calling
access_granted with the appropriate access mode at the beginning of
every exported operation. Otherwise protection will not take effect.
Methods like off_op_valid and off_mode_valid will be defined
for different data types so that assertions are easy to write.
<Off valid access operation and mode. >= (U->)
extern inline boolean_t off_op_valid( const off_op_t op) {
return (op >= OFF_OP_R && op <= OFF_OP_P);
}
extern inline boolean_t off_mode_valid( const off_mode_t m) {
return (m&~OFF_M_ALL)==0;
}
Definesoff_mode_valid,off_op_valid(links are to index).
\subsubsection{Protection \cpp{} source files}
Protection definitions are kept in a couple of files named
klib/prot.h and klib/prot.C.
<prot.h*>=
<Read the literate code instead warning. >
#ifndef __OFF_PROT_H
#define __OFF_PROT_H 1
<Off protection dependencies. >
<Off access operations. >
<Off access mode. >
<Off valid access operation and mode. >
<Off protection. >
<Off access rights. >
#ifdef __KERNEL__
<Off access checker. >
<off_AccessChecker::access_granted implementation. >
#endif // __KERNEL__
#endif // __OFF_PROT_H
<prot.C*>= <Read the literate code instead warning. > #include <klib/prot.h> // Exported interface. <off_Protection::randomizeimplementation. > <off_AccessChecker::protectimplementation. >
As we have seen, AccessChecker is used only inside the kernel. The
symbol __KERNEL__ will be defined only when compiling a kernel
image.
We used boolean_t, natural_t, and rand through the
code. We have to define such things.
<Off protection dependencies. >= (<-U) #include <assert.h> // for assert #include <flux/types.h> // for boolean_t natural_t et al. #include <stdlib.h> // for rand
Finally, every source file will include an advice for those willing to read the compiler source code instead of the literate source code (this file).
<Read the literate code instead warning. >= (<-U <-U U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U-> U->) // // === DO NOT READ OR MODIFY THIS FILE === // // This is a source file generated by the noweb literate // programming tool. // // If you have the chance read the literate source code instead. //
A Protection object will specify the protection of a given
Resource for different access modes.
A Rights object shows the access rights held by the user
accessing the resource.
AccessChecker objects implement the policy by which
access is granted or denied for a given Protection, access
Rights and access mode.
\end{itemize}
The meaning of Protection is then defined by the AccessChecker
object. Every time the system wants to know whether access is granted
or denied, it calls the resource access_granted method (which is
delegated to the AccessChecker with the user Rights and access
op). To change the protection the (also delegated) method
protect is called instead.
<Off access checker. >= (U->)
// Checks access for user operations on kernel resources.
//
class off_AccessChecker {
public:
// Checks for access rights for the given operation.
static boolean_t access_granted( const off_op_t op, const off_Rights &r,
const off_Protection &p );
// Changes protection.
static void protect(off_Protection &old, const off_Protection &now,
const off_mode_t m);
};
Definesoff_AccessChecker(links are to index).
Resources that need to be protected will delegate its protection
control to a system-wide AccessChecker. Different implementations
of the off_Protection interface will provide various protection
mechanisms including access control lists and capabilities. The
protection information will be kept in _prot2.6#214#>We will
prefix any data member of a class with the initial letter for the
class and an underscore (``_''). Those members which may be
in more than one class --like the reference counter-- will be just
prefixed by an underscore.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
node is not a
CompResource it can be considered to be a container.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
CompResources2.11
make_available is implemented
in subclasses of CompResource because resource units have
identifiers of different kinds.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
off_Version,
an object which keeps kernel version and compilation information. It
is described in section . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
To freeze (or melt) the state of a memory bank we must freeze (or
melt) dynamic members of the bank and also the page allocator being
used. Only the allocator itself is affected, page frames will remain
melted. That means that every page service (but for allocation and
deallocation) is still available. Applications can keep on running
while the memory bank is being transferred. The application issuing
the freeze is expected to eventually freeze its page frames too.
<off_MBank::freeze_state implementation. >= (U->)
// Freeze dynamic resource state at curbuf in buf with avail bytes.
err_t off_MBank::freeze_state(char *buf, char *&curbuf, size_t &avail,
char *&curto, size_t &toavail,
const off_prtl_id_t &frozen_domain)
{
assert(valid() && buf && curbuf);
if (!::freeze(m_name,buf,curbuf,avail) ||
!::freeze(m_url,buf,curbuf,avail))
return ENOSPC;
set_domain(frozen_domain);
return m_alloc.freeze_state(buf,curbuf,avail,curto,toavail);
}
<Off memory bank implementation dependencies. >+= (U->) [<-D->] #include <klib/freeze.h> // for ::freeze, ::melt et al.
<off_MBank::melt_state implementation. >= (U->)
// Restores dynamic resource state.
err_t off_MBank::melt_state(char *&buf, size_t &bsize,
char *&from, size_t &size,
const off_prtl_id_t &melted_domain)
{
assert(valid() && buf && bsize>0);
m_name=::melt(m_name,buf,bsize);
m_url=::melt(m_url,buf,bsize);
set_domain(melted_domain);
return m_alloc.melt_state(buf,bsize,from,size);
}
\subsubsection{Freezing page frames}
We need to implement only these methods:
<Other protected methods of off_PFrame. >+= (<-U) [<-D]
// Copy static resource state in curbuf with toavail bytes.
virtual err_t copy_state(char *&curto, size_t &toavail) const;
// Restores the static state from a user supplied buffer.
virtual err_t restore_state(char *&buf, size_t &bsize);
// Restores dynamic resource state.
<off_PFrame::copy_state implementation. >= (U->)
// Copy static resource state in curbuf with toavail bytes.
err_t off_PFrame::copy_state(char *&curto, size_t &toavail) const
{
assert(valid());
if (curto){
if (toavail<sizeof(*this))
return ENOSPC;
*(off_PFrame*)curto = *this;
assert(((off_PFrame*)curto)->valid());
curto += sizeof(*this);
toavail-=sizeof(*this);
}
return EOK;
}
Note how in this one we take care that the reference to the container is not affected by the state restore.
<off_PFrame::restore_state implementation. >= (U->)
// Restores the static state from a user supplied buffer.
err_t off_PFrame::restore_state(char *&buf, size_t &bsize)
{
off_PFrame *other=(off_PFrame*)buf;
assert(valid());
if (buf){
off_MBank *bank=(off_MBank*)get_container();
if (bsize < sizeof(*this)){
return EINVAL;
}
assert(other->valid());
*this=*other;
set_container(bank);
buf+=sizeof(*this);
bsize-=sizeof(*this);
}
return EOK;
}
To dump a memory bank we use this operator.
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Dumps mbank state.
friend OStr &operator<<(OStr &s, off_MBank &m);
<off_MBank::operator<< implementation. >= (U->)
// Dumps mbank state.
OStr &operator<<(OStr &s, off_MBank &m)
{
s << *(off_HWCompResource*)&m << nl;
s << "name=" << m.nameof() << " url=" << m.get_url() << nl;
s << "first=" << fmt("%08x",m.get_first()) <<
" last=" << fmt("%08x",m.get_last()) <<
fmt(" (%d pages)",m.get_npgs()) << ":" << nl;
for (vm_size_t sz=0;(sz=m.get_pgsize(sz)) != 0; ){
if (sz)
s << ",";
s << fmt("%d",sz);
}
s << ")" << nl;
for (vm_offset_t p=m.get_first(),old=p; p < m.get_last() ;
old=p,p+=m.get_pgsize()){
if (p==m.get_first() ||
(m+old)->get_domain() != (m+p)->get_domain()){
if (p>m.get_first())
s<<" - "<<fmt("%05x",m.a2p(p-m.get_pgsize())) <<"]" <<nl;
if ((m+p)->get_domain() == OFF_PRTL_NULL)
s << "available [" << fmt("%05x",m.a2p(p));
else if ((m+p)->get_domain() == nd.get_domain())
s << "kernel [" << fmt("%05x",m.a2p(p));
else
s << (m+p)->get_domain() << "[" << fmt("%05x",m.a2p(p));
}
}
s << " - " << fmt("%05x",m.a2p(m.get_last())) << "]" << nl;
return s;
}
<Off memory bank implementation dependencies. >+= (U->) [<-D] #include <klib/str.h> // for OStr et al.
A similar operator is provided for page frames.
<Other public methods of off_PFrame. >+= (<-U) [<-D]
// Dumps pframe state.
friend OStr &operator<<(OStr &s, off_PFrame &p);
Its implementation dumps more than a full memory bank dump would do.
<off_PFrame::operator<< implementation. >= (U->)
// Dumps pframe state.
OStr &operator<<(OStr &s, off_PFrame &p)
{
s << (off_HWResUnit)p << fmt("bits=%02x",p.p_bits) << nl;
return s;
}
<Off page frame implementation dependencies. >+= (U->) [<-D] #include <klib/str.h> // for OStr et al.
We implement the second one because the first one can be implemented by system users which prefer it that way. Besides, by implementing the second one applications can still run until they produce further physical memory allocation requests.
We must implement these methods to support freeze:
<Other protected methods of off_MBank. >+= (<-U) [<-D->]
// These are for freeze:
// Freeze dynamic resource state at curbuf in buf with avail bytes.
virtual err_t freeze_state(char *buf, char *&curbuf, size_t &avail,
char *&curto, size_t &toavail,
const off_prtl_id_t &frozen_domain);
// Copy static resource state in curbuf with toavail bytes.
virtual err_t copy_state(char *&curto, size_t &toavail) const;
as well as these others to support melt:
<Other protected methods of off_MBank. >+= (<-U) [<-D->]
// These are for melt:
// Does resource cleanup. No external resource should be used after
// return.
virtual err_t cleanup(void);
// Restores the static state from a user supplied buffer.
virtual err_t restore_state(char *&buf, size_t &bsize);
// Restores dynamic resource state.
virtual err_t melt_state(char *&buf, size_t &bsize, char *&from, size_t &size,
const off_prtl_id_t &melted_domain);
The most simple are cleanup and copy_state. Any existing
memory bank inspector is also deleted.
<off_MBank::cleanup implementation. >= (U->)
// Does resource cleanup. No external resource should be used after
// return.
err_t off_MBank::cleanup(void)
{
assert(valid() && m_name && m_url);
delete[] m_name;
delete[] m_url;
return EOK;
}
<off_MBank::copy_state implementation. >= (U->)
// Copy static resource state in curbuf with toavail bytes.
err_t off_MBank::copy_state(char *&curto, size_t &toavail) const
{
assert(valid());
if (curto){
if (toavail<sizeof(*this))
return ENOSPC;
*(off_MBank*)curto = *this;
curto += sizeof(*this);
toavail-=sizeof(*this);
}
return EOK;
}
The counterpart, restore_state, uses a copy operator tailored not to
those members which must remain unaltered (m_attrs).
<Other public methods of off_MBank. >+= (<-U) [<-D->]
// Copy memory bank contents.
const off_MBank &operator=(const off_MBank &other);
It also uses can_melt to know if the frozen memory
bank can be melted on this one.
<Other protected methods of off_MBank. >+= (<-U) [<-D]
// Is the other bank meltable at this one?
boolean_t can_melt(const off_MBank *other) const;
<off_MBank::restore_state implementation. >= (U->)
// Restores the static state from a user supplied buffer.
err_t off_MBank::restore_state(char *&buf, size_t &bsize)
{
off_MBank *other=(off_MBank*)buf;
assert(valid());
if (buf){
if (bsize < sizeof(*this) || !can_melt(other)){
return EINVAL;
}
assert(other->valid());
*this=*other;
bsize-=sizeof(*this);
buf+=sizeof(*this);
}
return EOK;
}
<off_MBank::operator= implementation. >= (U->)
// Copy memory bank contents.
const off_MBank &off_MBank::operator=(const off_MBank &other)
{
*(off_HWCompResource*)this = *(off_HWCompResource*)&other;
assert(valid() && other.valid());
m_alloc = other.m_alloc;
m_url=other.m_url;
m_name=other.m_name;
m_mdep=other.m_mdep;
return *this;
}
The frozen memory bank can be melted only if addresses contained in the frozen bank are valid for us. Otherwise a ``bigger'' memory bank might be needed. Besides, we should check that we do not loose memory features.
<off_MBank::can_melt implementation. >= (U->)
// Is the other bank meltable at this one?
boolean_t off_MBank::can_melt(const off_MBank *other) const
{
// NB: Perhaps we should relax and allow `degraded' melts
// where some features are missing. XXX
// Besides, only addresses for allocated pages need to be
// taken into account, not every address.
assert(valid() && other && other->valid());
return ( ! ((other->is_dmable() && !is_dmable()) ||
(other->is_iomapable() && !is_iomapable()) ||
(other->is_persistent() && !is_persistent()) )
&& (other->get_first() >= get_first() &&
other->get_last() <= get_last() &&
other->get_mat() >= get_mat() &&
other->get_pgsize(0) >= get_pgsize(0) &&
other->get_pgsize(0) % get_pgsize(0) == 0));
}
Note how, with respect to page sizes, we admit melts whenever the page size in the frozen bank is an integral multiple of the page size in this bank (this may require some adjustment4.4#2702#>grouping some pages to ``emulate'' a single page of the melted bank.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Thus, a property can be defined as follows:
<Off shuttle property. >= (U->)
// A shuttle property.
//
class off_ShtlProp {
public:
<Other public methods of off_ShtlProp. >
private:
off_id_t p_val; // Property value.
off_ShtlPropSrv *p_srv; // Property server.
off_ShtlPropSrv *p_swsrv; // Property server for switch() (cached).
};
Definesoff_ShtlProp(links are to index).
<Off shuttle property dependencies. >= (U->) [D->] #include <klib/err.h> // for err_t #include <klib/ids.h> // for OFF_PRTL_NULL et al. #include <flux/types.h> // for boolean_t natural_t et al.
Where the property server must implement this interface\footnote{An abstract class could do the job, but GNU C++ signatures provide subtype polymorphism independently from the inheritance hierarchy.:
<Off shuttle property server. >= (U->)
// The interface of a property server.
//
signature off_ShtlPropSrv {
// Switches property values.
// Returns either 0 or an error code.
err_t pswitch(const off_id_t &to, const off_shtl_id_t &s);
// Set or clear the property at a given shuttle.
err_t pset(const off_id_t &pval,const off_shtl_id_t &s);
void pclr(const off_id_t &pval, const off_shtl_id_t &s);
// Is pswitch used?
boolean_t needs_switch(void);
};
Definesoff_ShtlPropSrv(links are to index).
When a property is being created (i.e. defined), its value and server must be provided.
<Other public methods of off_ShtlProp. >= (<-U) [D->]
// Creates this property.
off_ShtlProp(off_ShtlPropSrv *srv, const off_id_t &val);
Although another constructor is provided to declare uninitialized properties.
<Other public methods of off_ShtlProp. >+= (<-U) [<-D->]
// Creates this property as unitialized
off_ShtlProp(void) :
p_val(OFF_ID_NULL)
{;}
The property calls the server needs_switch to determine if the
switch function is needed or not, as there are properties which do not
need a switch function (eg. those properties handled by the hardware,
like the IOPL, and those properties which are passive, like user
ids).
<off_ShtlProp::off_ShtlProp implementation. >= (U->)
off_ShtlProp::off_ShtlProp(off_ShtlPropSrv *srv, const off_id_t &val) :
p_val(val), p_srv(srv)
{
if (srv->needs_switch())
p_swsrv=srv;
else
*(void**)&p_swsrv=NULL;
}
The preperty value can be changed later at any time and can be consulted.
<Other public methods of off_ShtlProp. >+= (<-U) [<-D->]
// Sets a new value for this property.
inline void set_val(const off_id_t &val);
// Returns the property value.
inline const off_id_t &get_val(void) const;
<off_ShtlProp::set_valandget_valimplementation. >= (U->) // Sets a new value for this property. inline void off_ShtlProp::set_val(const off_id_t &val) { p_val = val; } // Returns the property value. inline const off_id_t &off_ShtlProp::get_val(void) const { return p_val; }
The property server, implementing the functions servicing the property, is reachable through the property.
<Other public methods of off_ShtlProp. >+= (<-U) [<-D->]
// Switches property values.
// Returns either 0 or an error code.
inline err_t pswitch(const off_shtl_id_t &s);
// Set or clear the property at a given shuttle.
err_t pset(const off_id_t &pval, const off_shtl_id_t &s);
<off_ShtlProp::pswitch implementation. >= (U->)
// Switches property values.
// Returns either 0 or an error code.
inline err_t off_ShtlProp::pswitch(const off_shtl_id_t &s)
{
if ((void*)p_swsrv!=NULL)
return p_swsrv->pswitch(p_val,s);
else
return EOK;
}
<off_ShtlProp::psetandpclrimplementation. >= (U->) // Set or clear the property at a given shuttle. err_t off_ShtlProp::pset(const off_id_t &pval, const off_shtl_id_t &s) { p_srv->pclr(p_val,s); p_val=pval; return p_srv->pset(p_val,s); }
There are two kind of property servers, one using procedure calls for in-kernel properties and another using portal calls for off-kernel properties. The first one can be implemented using a pointer to an object matching the property server interface shown above. The second one can use this wrapper.
<Off off-kernel shuttle property server. >= (U->)
// A user-serviced property
//
class off_uShtlPropSrv {
public:
<Other public methods of off_uShtlPropSrv. >
// Switches property values.
// Returns either 0 or an error code.
err_t pswitch(const off_id_t &to, const off_shtl_id_t &s);
// Set or clear the property at a given shuttle.
err_t pset(const off_id_t &pval,const off_shtl_id_t &s);
void pclr(const off_id_t &pval, const off_shtl_id_t &s);
// Is pswitch used?
boolean_t needs_switch(void);
};
Definesoff_uShtlPropSrv(links are to index).
Properties handled inside the kernel must be well-defined at every node in the network, user properties do not need to. For user defined properties, the portal servicing property switches is carried along with the property value in every shuttle. Every kernel can then blindly jump through that portal at any node. Of course, it is wise to have those portals replicated at every node because they will be used every shuttle switch.
The user portal servicing a property is given when the user property is being created.
<Other public methods of off_uShtlPropSrv. >= (<-U)
// Creates a user property server dispatching to the given portal.
off_uShtlPropSrv(const off_prtl_id_t &uobj, boolean_t needs_switch=FALSE);
A flag is also given so that the shuttle server could know if it is
necessary at all to callpswitch (to prepare the property for use).
Call a property switch function, pswitch, supplying the
property value as an argument every time a shuttle using it is about
to run.
Call a property set function, pset, supplying the value
being set every time the property value is (re)set in a shuttle.
Call a property cleanup function, pclr, supplying the value
being cleaned up every time the property value is about to be set.
\end{itemize}
The first function is intended to be used as a command5.3#5422#>See the command pattern.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
s).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Francisco J. Ballesteros