Resources can be frozen and melted using
<Other public methods of off_Resource. >+= (<-U) [<-D->]
// Freezes it. Returns in the frozen state in to (if non-null) and size.
// Additional state is stored in user provide buffer buf.
// Any attempt to access this frozen resource will cause an exception
// delivered to frozen_domain
virtual err_t freeze(char *&buf, size_t &bsize,
char *&to,size_t &size,
const off_prtl_id_t &frozen_domain,
boolean_t sign_it=FALSE);
// Melts a resource. Both the frozen resource and the additional
// state obtained by freeze must be provided. If from is null it is
// assumed that the resource already has its frozen state.
// The domain is restored as specified by melted_domain
virtual err_t melt(char *&buf, size_t &bsize,
char *&from, size_t &size,
const off_prtl_id_t &melted_domain,
boolean_t was_signed=FALSE);
As we saw before, the two primitives freeze and melt permit
access to the state of a resource and recreation of a resource whose
state is known. This can be used to migrate, replicate and make
physical resources persist.
The semantic of both operations is as follows:
melt operations). Any attempt to
operate on a frozen resource will cause a FROZEN exception to be
raised. The system call causing the exception will await until the
exception is handled (indeed, the exception will be handled using
the flow of control of the call).
A positive answer from the exception handler allows the call to
proceed. A negative answer from the exception makes the system call
fail.
Starting with the most simple methods of the implementation, we
provide a default implementation for is_freezeable and
is_frozen using a flags field. In this way these tests can be
fast.
<Other public methods of off_Resource. >+= (<-U) [<-D->]
// Returns FALSE if it's not frozen.
boolean_t is_frozen(void) const {
return r_flags & OFF_RF_FROZEN; }
// Returns FALSE if it's not freezeable.
virtual boolean_t is_freezeable(void) const {
return (r_flags & OFF_RF_FREEZABLE) && !is_frozen();
}
<Other protected members of off_Resource. >= (<-U)
u_char r_flags; // Resource flags.
<Initialize other aggregate members of off_Resource. >= (<-U)
r_flags(0)
We must define now the symbols used above.
<Off resource flag bits. >= (<-U) // Resource flag bits. const u_char OFF_RF_FREEZABLE=0x1; // Is this resource freezeable? const u_char OFF_RF_FROZEN =0x2; // Is this resource frozen?
DefinesOFF_RF_FREEZABLE,OFF_RF_FROZEN(links are to index).
Another couple of methods is provided so that these bits could be set appropriately.
<Other protected methods of off_Resource. >+= (<-U) [<-D->]
// Sets/Clears the freezeable bit.
void set_rf_freezeable(void) { r_flags |= OFF_RF_FREEZABLE; }
void clr_rf_freezeable(void) { r_flags &= ~OFF_RF_FREEZABLE; }
// Sets/Clears the frozen bit.
void set_rf_frozen(void) { r_flags |= OFF_RF_FROZEN; }
void clr_rf_frozen(void) { r_flags &= ~OFF_RF_FROZEN; }
The remaining six bits of r_flags remain unused. They can be used
in different ways by subclasses of Resource.
To freeze a system resource we must rely on specific functions which know how to freeze the resource state.
First, the resource state is frozen using the already used resource
memory. But additional (dynamic) state which cannot be kept inside
the resouce itself is stored in the user supplied buffer buf. We
will fill it up using a pointer (curbuf) to the trailing free
space in buf and a count of the number of bytes avail.
<off_Resource::freeze local variables. >= (U->) [D->]
char *curbuf=buf; // Unused portion of buf.
size_t avail=bsize; // Avilable # of bytes in buf.
A resource specific method freeze_state will do the job. The
default implementation assumes that there is no dynamic state.
<Other protected methods of off_Resource. >+= (<-U) [<-D->]
// Freeze dynamic resource state in curbuf 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 );
<off_Resource::freeze_state implementation. >= (U->)
// Freeze dynamic resource state in curbuf with avail bytes.
err_t off_Resource::freeze_state(char *buf, char *&curbuf,
size_t &avail,
char *&curto, size_t &toavail,
const off_prtl_id_t &frozen_domain )
{
assert(buf && curbuf && avail > 0);
(void)buf; (void)curbuf; (void)avail; (void)curto; (void)toavail;
(void)frozen_domain;
return EOK;
}
<Implementation of other methods ofoff_Resource. >= (<-U) [D->] <off_Resource::freeze_stateimplementation. >
Second, an additional buffer to is supplied by the user when the
resource state frozen on-site (i.e. using its own memory) must be
copied to a user buffer (to). We use another couple of local
variables to handle this buffer.
<off_Resource::freeze local variables. >+= (U->) [<-D->]
char *curto=to;
size_t toavail=size;
In this case, copy_state will do the job.
<Other protected methods of off_Resource. >+= (<-U) [<-D->]
// Copy static resource state in curbuf with toavail bytes.
virtual err_t copy_state(char *&curto, size_t &toavail) const =0;
Once these two functions are implemented, we can write freeze as
follows.
<off_Resource::freezeimplementation. >= (U->) // Freezes it. Returns in the frozen state in to (if non-null) and size. // Additional state is stored in user provide buffer buf. // Any attempt to access this frozen resource will cause an exception // delivered to frozen_domain err_t off_Resource::freeze(char *&buf, size_t &bsize, char *&to,size_t &size, const off_prtl_id_t &frozen_domain, boolean_t sign_it ) { err_t err=EOK; <off_Resource::freezelocal variables. > w_lock(); if (is_freezeable()){ set_rf_frozen(); <Allocate space atcurbufinavailbytes to storesigstart. > <Identify the frozen resource incurbufof lengthavail. > if ((err=freeze_state(buf,curbuf,avail,curto,toavail,frozen_domain)) || (err=copy_state(curto,toavail))){ natural_t i=curbuf-buf, j=curto-to; w_unlock(); melt(buf,i,to,j,get_domain()); return err; } <Recordsigstartatcurbufinbuf. > if (sign_it){ <Sign frombuftocurbufandtoinavailbytes. > } set_domain(frozen_domain); w_unlock(); bsize=curbuf-buf; if (to) size=curto-to; return EOK; } else { w_unlock(); return EFROZEN; } }
Note how the lock prevents race conditions on concurrent freeze
operations. The is_freezeable test prevents an already frozen resource
to be frozen again. That is considered to be an error:
<Other Off error numbers. >= EFROZEN // Resource is frozen.
The dynamic frozen state buffer supplied by the user will always start with two strings: one identifying the frozen resource and another identifying the architecture format2.12.
<Identify the frozen resource incurbufof lengthavail. >= (<-U) if (!::freeze(get_magic().nameof(),curbuf,avail) || !::freeze(off_Version::arch,curbuf,avail) ) return ENOSPC;
<Off resource implementation dependencies. >= (<-U) [D->] #include <assert.h> // for assert #include <klib/Version.h> // for off_Version #include <klib/freeze.h> // for freeze
Finally, a secure digital signature can be requested. To quickly locate it we
store the index in buf where the signature starts.
<off_Resource::freeze local variables. >+= (<-U) [<-D]
unsigned32_t *sigstart; // End of frozen data/ start of signature
<Allocate space atcurbufinavailbytes to storesigstart. >= (<-U) sigstart=(unsigned32_t *)curbuf; if (avail<=sizeof(unsigned32_t)) return ENOSPC; curbuf+=sizeof(unsigned32_t); avail-=sizeof(unsigned32_t);
<Recordsigstartatcurbufinbuf. >= (<-U) assert(sigstart); *sigstart=curbuf-buf;
If requested, we sign the user buffer so that we could ensure data
integrity whenever the node melts. We use the utility sign which
is described below.
<Sign frombuftocurbufandtoinavailbytes. >= (<-U) if((to && !sign(curbuf,avail,to,curto-to,buf,curbuf-buf)) || (!to && !sign(curbuf,avail,buf,curbuf-buf)) ){ natural_t i=curbuf-buf,j=curto-to; w_unlock(); melt(buf,i,to,j,get_domain()); return ENOSPC; }
<Off resource implementation dependencies. >+= (<-U) [<-D->] #include <klib/sign.h> // for sign
The signature can be checked when the resource is melted by locating
it in the buffer using sigstart, which was stored at the beginning
of the buffer and using the sign_ok utility (described later).
<from(lensize) andbuf(bsize) integrity is ok. >= (U->) ((from) ? sign_ok(buf+(*(unsigned32_t*)buf),bsize-(*(unsigned32_t*)buf), from,size,buf,bsize) : sign_ok(buf+(*(unsigned32_t*)buf),bsize-(*(unsigned32_t*)buf),buf,bsize))
Be a resource frozen or not, a frozen image can be melted on it. We we melt a frozen image into a fresh resource we will throw away any previous state and reset the resource state to that found in the frozen image. If the resource was frozen, its frozen state is melted.
To melt a system resource we must first check the integrity of the
data (if it was signed on freeze). Then check that the image
correspond to the resource type. If everything looks fine we can
proceed by melting the resource static and dynamic state.
<off_Resource::meltimplementation. >= (U->) // Melts a resource. Both the frozen resource and the additional // state obtained by freeze must be provided. If from is null it is // assumed that the resource already has its frozen state on-site. // The domain is restored as specified by melted_domain err_t off_Resource::melt(char *&buf, size_t &bsize, char *&from, size_t &size, const off_prtl_id_t &melted_domain, boolean_t was_signed ) { assert(buf && bsize >0); if (bsize < sizeof(unsigned32_t)) return EINVAL; if (was_signed && ! <from(lensize) andbuf(bsize) integrity is ok. >) return EPERM; <Ensurebuf(bsizebytes) has this resource kind. > w_lock(); if (!is_frozen()) cleanup(); // forget about the old state. if (from) restore_state(from,size); melt_state(buf,bsize,from,size,melted_domain); clr_rf_frozen(); set_domain(melted_domain); w_unlock(); return EOK; }
We first ensure that we are melting a node for the native architecture. If that is not the case, we assume that it is a foreign-architecture frozen node and try to assimilate it to the native format.
<Ensurebuf(bsizebytes) has this resource kind. >= (<-U) // Are we melting this kind of resource in the native format? if(strncmp(::melt(buf,buf,bsize),kindof(),strlen(kindof())) || strncmp(::melt(buf,buf,bsize),off_Version::arch,strlen(off_Version::arch))){ if (!assimilate(kindof(),off_Version::arch,buf,size)) return EINVAL; }
<Off resource implementation dependencies. >+= (<-U) [<-D] #include <string.h> // for strncmp et al.
Where assimilate must be implemented for concrete instances of
Resource.
<Other public methods of off_Resource. >+= (<-U) [<-D->]
virtual err_t assimilate(const char *kind,
const char *arch, char *&buf, size_t &size);
<off_Resource::assimilate implementation. >= (U->)
err_t off_Resource::assimilate(const char *kind,
const char *arch, char *&buf,
size_t &size)
{
(void)kind; (void)arch; (void)buf; (void)size;
return ENOSYS;
}
<Implementation of other methods ofoff_Resource. >+= (<-U) [<-D->] <off_Resource::assimilateimplementation. >
Subclasses of Resource must implement cleanup, to do any
internal cleanup prior to discarding state. The default implementation
assumes that there is nothing to clean up.
<Other protected methods of off_Resource. >+= (<-U) [<-D->]
// Does resource cleanup. No external resource should be used after
// return.
virtual err_t cleanup(void) { return EOK; }
They must also implement restore_state to restore static state
from a user supplied buffer, and melt_state to restore any dynamic
state. The default implementation of melt_state assumes that there
is no dynamic state to melt.
<Other protected methods of off_Resource. >+= (<-U) [<-D->]
// Restores the static state from a user supplied buffer.
virtual err_t restore_state(char *&buf, size_t &bsize)=0;
// 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);
<off_Resource::melt_state implementation. >= (U->)
// Restores dynamic resource state.
err_t off_Resource::melt_state(char *&buf,
size_t &bsize, char *&from,
size_t &size,
const off_prtl_id_t &melted_domain)
{
assert(buf && bsize>0);
(void)buf; (void)bsize; (void)from; (void)size; (void)melted_domain;
return EOK;
}
<Implementation of other methods ofoff_Resource. >+= (<-U) [<-D->] <off_Resource::melt_stateimplementation. >
All the implementations shown above is included with remaining resource implementation code.
<Implementation of other methods ofoff_Resource. >+= (<-U) [<-D] <off_Resource::freezeimplementation. > <off_Resource::meltimplementation. >
Another method make_melted should be called before operation on
the resource. It is responsible of raising FROZEN exceptions when
appropriate.
<Other public methods of off_Resource. >+= (<-U) [<-D->]
// Ensures the resource is not frozen by raising FROZEN
// exceptions when appropriate.
err_t make_melted(void) {
return (!is_frozen()) ? EOK : request_melt();
}
The request_melt method simply raises a FROZEN exception. The
handler provided error code determines is the call may proceed. The
default action is to abort.
<Other protected methods of off_Resource. >+= (<-U) [<-D]
// Raises a FROZEN exception.
virtual err_t request_melt(void) {
return ::request_melt(get_domain(),get_magic(),get_id());
}
<Off resource dependencies. >+= (<-U) [<-D->] #include <klib/freeze.h>
Where ::request_melt is a utility to raise FROZEN exceptions.
<request_melt utilities. >= (U->)
// Raises a FROZEN exception.
err_t request_melt(const off_prtl_id_t &ex,
const off_Magic kind, const off_id_t &eid);
<request_melt utilities dependencies. >= (U->)
#include <klib/ids.h> // for prtl_id_t et al.
#include <klib/err.h> // for err_t and error numbers.
#include <klib/Magic.h> // for off_Magic.
The implementation delivers an exception to the user supplying (in
addition to the exception type, m_type) the kind of resource and
the resource identifier.
<Off user-kernel messages. >= [D->]
// Frozen message.
struct off_FrozenReq : public off_MsgReq {
off_Magic m_obj; // Object kind.
off_id_t m_id; // Object id.
// Creates a frozen request.
off_FrozenReq(const off_Magic obj, const off_eu_id_t id) :
off_MsgReq(OFF_EX_FROZEN), m_obj(obj), m_id(id) {;}
};
Definesoff_FrozenReq(links are to index).
<Off user-kernel messages dependencies. >= [D->] #include <klib/ids.h> // for off_eu_id_t et al. #include <klib/Magic.h> // for off_Magic
The user is expected to return any of: EOK when the call should be
able to proceed, EFROZEN when the call should be aborted, and
ENOPRTL when the resource should be considered to be missing until
it is melted again.
<Off user-kernel messages. >+= [<-D->]
struct off_FrozenRep : public off_MsgRep {
off_id_t m_id; // New relocation (if known) or OFF_ID_NULL
err_t m_err;
off_FrozenRep(void){;}
off_FrozenRep(const off_eu_id_t id) : m_id(id) {;}
};
Definesoff_MissingRep(links are to index).
<request_melt utility implementation. >= (U->)
// Raises a FROZEN exception.
err_t request_melt(const off_prtl_id_t &ex,
const off_Magic kind, const off_id_t &eid)
{
err_t e=EOK;
(void)ex; (void)kind; (void)eid;
#if 0 // XXX include it
off_FrozenReq m(kind, eid);
off_FrozenRep r;
if (ex == OFF_PRTL_NULL)
return ENOPRTL;
if ((e=prtl.kpct(ex,off_Shtl::self(),sizeof(m),sizeof(r),&m,&r,0)))
return e;
return (r.m_err) ? EFROZEN : EOK;
#endif
return e;
}
<request_melt utility implementation dependencies. >= (U->)
//#include <prtl/ex.h> // for off_Frozen{Req|Rep} XXX include it
//#include <prtl/PrtlSrv.h> // for prtl.
//#include <shtl/Shtl.h> // for Shtl and self.
These utilities are defined in klib/freeze.h, which must be
included by CompResource.
<Off compound resource dependencies. >+= (<-U) [<-D] #include <klib/freeze.h> // for ::request_melt