next up previous contents
Next: 4.3.0.1 Idle Up: 4. Exporting the hardware Previous: 4.2.2.2 Freezing timer servers

   
4.3 Processors

\Note{Include priority levels. Note that each processor must act as a referee for interrupt priorities. That means that no new shuttle can honor a priority level lower than the lowest interrupt.... Think about it.  

A processor is a composite hardware resource which allocates time slices.

<Off processor. >= (U->)
// A processor. 
//
class off_Processor : public off_HWCompResource {
public:
  // Returns a pointer to the time slice allocator. 
  virtual off_RqAllocator *get_allocator(void);

  <Other public methods of off_Processor. >
protected:
  <Other protected methods of off_Processor. >
private:
  off_RqAllocator p_alloc;      // Time slice allocator. 
  <Other private members of off_Processor. >
  <Other private methods of off_Processor. >
};
Defines off_Processor (links are to index).

<off_Processor::get_allocator implementation. >= (U->)
inline off_RqAllocator *off_Processor::get_allocator(void) {
  assert(valid()); return &p_alloc;
}

Being a subclass of CompResource we must implement make_available. This method does not have sense for run queue slots thus it simply returns an error code4.8.

<Other public methods of off_Processor. >= (<-U) [D->]
// We may wish to cache a resource unit.
// Returns error code if the resource is not available. Zero otherwise.
err_t make_available(off_id_t &u) { 
  (void)u; 
  return EINVAL; 
}

<Off processor dependencies. >= (U->) [D->]
#include <flux/types.h>         // for boolean_t natural_t et al.
#include <klib/ids.h>           // for natural_t et al.
#include <klib/HWCompResource.h> // for off_HWCompResource
#include <klib/err.h>            // for err_t 
#include <klib/prot.h>          // for off_Protection
#include <klib/Magic.h>          // for magic numbers
#include <hw/RqAllocator.h>      // for off_RqAllocator

Initially, processors are created with default values for its members.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
off_Processor(void) : 
  off_HWCompResource(off_Protection(),OFF_PRTL_NULL,
                     OFF_ID_NULL, OFF_MAGIC_PROC),
  <Initialize p_alloc. >,
  <Initialize other aggregate members of off_Processor. >
{;}

We must define the new magic number used before.

<Off magic numbers. >= [D->]
OFF_MAGIC_PROC, // Processor magic nb.

We can now add another case to the implementation of Magic::nameof.

<off_Magic::nameof case for m_numbers. >= [D->]
case OFF_MAGIC_PROC:
  return "off_Processor";

Processor users can check their references to processors using this method.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
// Does this look like a Processor?
boolean_t valid(void) const { return get_magic() == OFF_MAGIC_PROC; }

After instantiation, start should be used to start processor operation.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
// Starts normal operation for this processor.
err_t start( const off_mdepProcessor &mdep, const off_id_t &id );

<Off processor dependencies. >+= (U->) [<-D->]
#include <hw/mdep/mProcessor.h> // for off_mdepProcessor

<off_Processor::start implementation. >= (U->)
// Starts normal operation for this processor.
err_t off_Processor::start( const off_mdepProcessor &mdep, const off_id_t &id)
{
  assert(valid());
  set_id(id); 

  <Start other members of off_Processor according to mdep. >

  kcout<< "Proc   #"<<id<< fmt(" (%d slots) ",get_nslots())<< "started."<< nl;

  <Start idle shuttle on non-boot processors. >
  return EOK;

}

<Off processor implementation dependencies. >= (U->) [D->]
#include <assert.h>             // for assert
#include <klib/str.h>           // for kcout.

Here we used get_nslots to return the maximum number of time slices. It is a system limit as we will see later.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
// Returns the max. number of ts. 
natural_t get_nslots(void) const { return OFF_NRQS_MAX; }

A new member, m_mdep will store the boot time information, which is needed because it provides common information useful to processor users.

<Other private members of off_Processor. >= (<-U) [D->]
off_mdepProcessor   p_mdep;         // Mach. dep. information

<Start other members of off_Processor according to mdep. >= (<-U) [D->]
p_mdep=mdep;

Initially, before the processor is started it is initialized with default values for the boot processor.

<Initialize other aggregate members of off_Processor. >= (<-U) [D->]
p_mdep(0),

To find the current processor when SMP support is enabled, self always returns a pointer to the current processor. It is a machine dependent operation.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
// Returns the current processor
static off_Processor *self(void);

<off_Processor::self implementation. >= (U->)
// Returns the current processor
off_Processor *off_Processor::self(void) 
{
  return &nd.get_proc(off_mdepProcessor::get_proc_id());
}

<Off processor implementation dependencies. >+= (U->) [<-D->]
#include <node/Node.h> 

Finally, as we said when we discussed traps and interrupts, each Processor contains a couple of event tables, one for traps and another one for interrupts.

<Other private members of off_Processor. >+= (<-U) [<-D->]
static off_TrapTbl *p_traps;           // Processor trap table.
static off_IntTbl  *p_irqs;            // Processor interrupt table.

<Off processor dependencies. >+= (U->) [<-D->]
#include <hw/EventTbl.h>        // for off_TrapTbl, off_IntTbl et al. 

<Off processor static members. >= (U->) [D->]
off_TrapTbl *off_Processor::p_traps=NULL; // Processor trap table.
off_IntTbl  *off_Processor::p_irqs=NULL; // Processor interrupt table.

Such tables are not physically contained in processor instances; they are shared among different processors in the same node.

<Start other members of off_Processor according to mdep. >+= (<-U) [<-D->]
if (!p_traps)
  p_traps = new off_TrapTbl(this);
if (!p_irqs)
  p_irqs  = new off_IntTbl(this);

Notice that they share the identifier of the processor they are in. A reference to such tables can be obtained.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
// Return a reference to the trap/interrupt event tables.
off_TrapTbl *get_traps(void);
off_IntTbl  *get_irqs(void);

They simply return the pointers kept.

<off_Processor::get_traps and get_irqs implementation. >= (U->)
// Return a reference to the trap/interrupt event tables.
inline off_TrapTbl *off_Processor::get_traps(void) { return p_traps; }
inline off_IntTbl  *off_Processor::get_irqs(void)  { return p_irqs; }

Another couple of methods is provided so that processor users could reference individual traps and interrupts without being aware of trap and interrupt tables.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
off_Trap *get_trap(natural_t t);
off_Irq  *get_irq(natural_t  i);

<off_Processor::get_trap and get_irq implementation. >= (U->)
inline off_Trap *off_Processor::get_trap(natural_t t) {
  return (*p_traps)+t;
}
inline off_Irq *off_Processor::get_irq(natural_t i) {
  return (*p_irqs)+i;
}

Sometimes the kernel is interested in inhibiting/enabling interrupts in the current processor, some static methods are provided for that purpose. Besides, the processor can be halted.

<Other public methods of off_Processor. >+= (<-U) [<-D->]
// Inhibits interrupts.
static void clri(void);
// Enables interrupts.
static void seti(void);
// Are interrupts enabled?
static boolean_t has_clri(void) ;
// Halts the processor.
static void halt(void);

All these methods are implemented by the machine dependent processor.

<off_Processor::cli sti is_cli and hlt implementation. >= (U->)
// Inhibits interrupts.
inline void off_Processor::clri(void)    { off_mdepProcessor::clri(); }
// Enables interrupts.
inline void off_Processor::seti(void)    { off_mdepProcessor::seti(); } 
// Are interrupts enabled?
inline boolean_t off_Processor::has_clri(void)  { 
  return off_mdepProcessor::has_clri();
} 
// Halts the processor.
inline void off_Processor::halt(void)     { off_mdepProcessor::halt(); } 

\subsection{Processor registers}

Processors (actually machine dependent processors) define a data type to represent the processor register set. Such data type is exported to Processor users. The machine independent part only assumes that the processor has at least a program counter and a stack pointer.

<Off processor registers. >= (U->)
// Processor registers.
//
class off_ProcRegs {
public:
  off_mdepProcRegs p_mdep;      // Actual registers. 

  // Creates a  set of processor registers.
  off_ProcRegs(void): p_mdep() {assert(sizeof(p_mdep)==sizeof(off_ProcRegs));}

  // Creates a set of processor registers for a existing
  // machine dependent register set. 
  void *operator new( size_t s, void *p) { (void)s; return p; }

  // Set or get the PC/SP/EF
  void set_pc(register_t val) { p_mdep.set_pc(val); }
  void set_sp(register_t val) { p_mdep.set_sp(val); }
  register_t    get_pc(void)  const { return p_mdep.get_pc(); }
  register_t    get_sp(void)  const { return p_mdep.get_sp(); }
};

Defines off_ProcRegs (links are to index).

\subsection{Scheduling facilities}

Processors are multiplexed using run queues of processor time slices. Each run queue slot has an identifier corresponding to the shuttle which should run in it.

<Off run queue slot. >= (U->)
class off_RunqSlot : public off_HWResUnit {
public:
  // Get/Set the shuttle for this slot. 
  inline const off_shtl_id_t &get_shtl(void) const;
  inline void          set_shtl(const off_shtl_id_t &s);
  <Other public methods of off_RunqSlot. >
protected: 
  <Other protected methods of off_RunqSlot. >
private: 
  off_shtl_id_t r_shtl;         // Shuttle to be run.
  <Other private members of off_RunqSlot. >

};

Defines off_RunqSlot (links are to index).

<Off run queue slot dependencies. >= (U->) [D->]
#include <klib/ids.h>           // for OFF_PRTL_NULL et al.
#include <klib/HWResUnit.h>     // for off_HWResUnit
class off_Processor;            // to break the circular dependency

<Off run queue slot implementation dependencies. >= (U->)
#include <hw/Processor.h>       // for off_Processor

<off_RunqSlot::get_shtl and set_shtl implementation. >= (U->)
// Get/Set the shuttle for this slot. 
extern inline const off_shtl_id_t &off_RunqSlot::get_shtl(void) const { 
  return r_shtl; 
}
extern inline void  off_RunqSlot::set_shtl(const off_shtl_id_t &s) { 
  r_shtl = s; 
}

The run queue is always honored but for those cases where events with higher priority than that of the current shuttle happens. Besides While running, a shuttle might suffer an exception (either noticed by the hardware or artificially raised by the kernel) and the remaining ticks of the time slot will be automatically yield to the exception handler.

Each run queue slot is created usually with this constructor,

<Other public methods of off_RunqSlot. >= (<-U) [D->]
// Creates a pre-initialized run queue slot.
off_RunqSlot(const off_Protection &p, const off_prtl_id_t &domain,
             const off_shtl_id_t &s);

although a trivial constructor allows uninitialized run queue slot declarations.

<Other public methods of off_RunqSlot. >+= (<-U) [<-D->]
// An uninitialized run queue slot.
off_RunqSlot(void) : 
  off_HWResUnit(off_Protection(), 
                OFF_PRTL_NULL, 
                OFF_ID_NULL, 
                OFF_MAGIC_RQSLOT,NULL),
  r_shtl(OFF_SHTL_NULL)
{;}

The number of ticks per quantum is a machine dependent constant fixed at compile time.

<Off run queue slot dependencies. >+= (U->) [<-D->]
#include <klib/Magic.h>         // for magic numbers.
#include <klib/prot.h>          // for off_Protection

A new magic number is defined now.

<Off magic numbers. >+= [<-D]
OFF_MAGIC_RQSLOT,       // Off run queue slot magic number

We can now add another case to the implementation of Magic::nameof.

<off_Magic::nameof case for m_numbers. >+= [<-D]
case OFF_MAGIC_RQSLOT:
  return "off_RunqSlot";

Run queue slot users can check their references using this method.

<Other public methods of off_RunqSlot. >+= (<-U) [<-D->]
// Does this look like a RunqSlot?
boolean_t valid(void) const { return get_magic() == OFF_MAGIC_RQSLOT; }

The new container can also implement its get_umagic method.

<Other protected methods of off_Processor. >= (<-U) [D->]
virtual off_magic_t get_umagic(void) { return OFF_MAGIC_RQSLOT; }

That was a dummy constructor. The actual run queue slot constructor sets the slot protection, domain, shuttle to be run. Such constructor is to be used by user-level schedulers.

<off_RunqSlot::off_RunqSlot implementation. >= (U->)
// Creates a pre-initialized run queue slot.
off_RunqSlot::off_RunqSlot(const off_Protection &p, 
                           const off_prtl_id_t &domain,
                           const off_shtl_id_t &s) :
  off_HWResUnit(p,domain),
  r_shtl(s)
{ ; }



 
next up previous contents
Next: 4.3.0.1 Idle Up: 4. Exporting the hardware Previous: 4.2.2.2 Freezing timer servers
Francisco J. Ballesteros
1998-05-25