next up previous contents
Next: 2.4.5 Architecture awareness support Up: 2.4 System resources Previous: 2.4.3 Resource availability

   
2.4.4 Freezing resources

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:

frozen resource
A frozen resource maintains its identifier but is not accessible (but for 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.

melted resource
A melted resource is equal to the source (previously frozen) one. Its identifier matches the source identifier, as well as other state does.

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?
Defines OFF_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 of off_Resource. >= (<-U) [D->]
<off_Resource::freeze_state implementation. >

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::freeze implementation. >= (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::freeze local variables. >

  w_lock();
  if (is_freezeable()){
    set_rf_frozen();
    <Allocate space at curbuf in avail bytes to store sigstart. >
    <Identify the frozen resource in curbuf of length avail. >
    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;
    }
    <Record sigstart at curbuf in buf. >
    if (sign_it){
      <Sign from buf to curbuf and to in avail bytes. >
    }
    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 in curbuf of length avail. >= (<-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 at curbuf in avail bytes to store sigstart. >= (<-U)
sigstart=(unsigned32_t *)curbuf;
if (avail<=sizeof(unsigned32_t))
  return ENOSPC;
curbuf+=sizeof(unsigned32_t);
avail-=sizeof(unsigned32_t);

<Record sigstart at curbuf in buf. >= (<-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 from buf to curbuf and to in avail bytes. >= (<-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 (len size) and buf (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::melt implementation. >= (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 (len size) and buf (bsize) integrity is ok. >)
    return EPERM;

  <Ensure buf (bsize bytes) 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.

<Ensure buf (bsize bytes) 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 of off_Resource. >+= (<-U) [<-D->]
<off_Resource::assimilate implementation. >

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 of off_Resource. >+= (<-U) [<-D->]
<off_Resource::melt_state implementation. >

All the implementations shown above is included with remaining resource implementation code.

<Implementation of other methods of off_Resource. >+= (<-U) [<-D]
<off_Resource::freeze implementation. >
<off_Resource::melt implementation. >

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) {;}
};

Defines off_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) {;}
};

Defines off_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


next up previous contents
Next: 2.4.5 Architecture awareness support Up: 2.4 System resources Previous: 2.4.3 Resource availability
Francisco J. Ballesteros
1998-05-25