1  /*
   2   * \brief  Generic root component implementation
   3   * \author Norman Feske
   4   * \date   2006-05-22
   5   *
   6   * This class is there for your convenience. It performs the common actions
   7   * that must always be taken when creating a new session.
   8   */

   9  
  10  /*
  11   * Copyright (C) 2006-2013 Genode Labs GmbH
  12   *
  13   * This file is part of the Genode OS framework, which is distributed
  14   * under the terms of the GNU General Public License version 2.
  15   */

  16  
  17  #ifndef _INCLUDE__ROOT__COMPONENT_H_
  18  #define _INCLUDE__ROOT__COMPONENT_H_
  19  
  20  #include <root/root.h>
  21  #include <base/rpc_server.h>
  22  #include <base/heap.h>
  23  #include <ram_session/ram_session.h>
  24  #include <util/arg_string.h>
  25  #include <base/printf.h>
  26  
  27  namespace Genode {
  28  
  29     /**
  30      * Session creation policy for a single-client service
  31      */

  32     class Single_client
  33     {
  34        private:
  35  
  36           bool _used;

  37  
  38        public:
  39  
  40           Single_client() : _used(0) { }
  41  
  42           void aquire(const char *)
  43           {
  44              if (_used)
  45                 throw Root::Unavailable();

  46  
  47              _used = true;

  48           }

  49  
  50           void release() { _used = false; }

  51     }
;

  52  
  53  
  54     /**
  55      * Session-creation policy for a multi-client service
  56      */

  57     struct Multiple_clients
  58     {
  59        void aquire(const char *) { }
  60        void release() { }

  61     }
;

  62  
  63  
  64     /**
  65      * Template for implementing the root interface
  66      *
  67      * \param SESSION_TYPE  session-component type to manage,
  68      *                      derived from `Rpc_object`
  69      * \param POLICY        session-creation policy
  70      *
  71      * The `POLICY` template parameter allows for constraining the session
  72      * creation to only one instance at a time (using the `Single_session`
  73      * policy) or multiple instances (using the `Multiple_sessions` policy).
  74      *
  75      * The `POLICY` class must provide the following two functions:
  76      *
  77      * :`aquire(const char *args)`: is called with the session arguments
  78      *   at creation time of each new session. It can therefore implement
  79      *   a session-creation policy taking session arguments into account.
  80      *   If the policy denies the creation of a new session, it throws
  81      *   one of the exceptions defined in the `Root` interface.
  82      *
  83      * :`release`: is called at the destruction time of a session. It enables
  84      *   the policy to keep track of and impose restrictions on the number
  85      *   of existing sessions.
  86      *
  87      * The default policy `Multiple_clients` imposes no restrictions on the
  88      * creation of new sessions.
  89      */

  90     template <typename SESSION_TYPE, typename POLICY = Multiple_clients>
  91     class Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >, private POLICY
  92     {
  93        private:
  94  
  95           /*
  96            * Entry point that manages the session objects
  97            * created by this root interface
  98            */

  99           Rpc_entrypoint *_ep;
 100  
 101           /*
 102            * Allocator for allocating session objects.
 103            * This allocator must be used by the derived
 104            * class when calling the `new` operator for
 105            * creating a new session.
 106            */

 107           Allocator *_md_alloc;

 108  
 109        protected:
 110  
 111           /**
 112            * Create new session (to be implemented by a derived class)
 113            *
 114            * Only a derived class knows the constructor arguments of
 115            * a specific session. Therefore, we cannot unify the call
 116            * of its `new` operator and must implement the session
 117            * creation at a place, where the required knowledge exist.
 118            *
 119            * In the implementation of this function, the heap, provided
 120            * by `Root_component` must be used for allocating the session
 121            * object.
 122            *
 123            * If the server implementation does not evaluate the session
 124            * affinity, it suffices to override the overload without the
 125            * affinity argument.
 126            *
 127            * \throw Allocator::Out_of_memory  typically caused by the
 128            *                                  meta-data allocator
 129            * \throw Root::Invalid_args        typically caused by the
 130            *                                  session-component constructor
 131            */

 132           virtual SESSION_TYPE *_create_session(const char *args,
 133                                                 Affinity const &)

 134           {
 135              return _create_session(args);
 136           }

 137  
 138           virtual SESSION_TYPE *_create_session(const char *args)
 139           {
 140              throw Root::Invalid_args();
 141           }

 142  
 143           /**
 144            * Inform session about a quota upgrade
 145            *
 146            * Once a session is created, its client can successively extend
 147            * its quota donation via the `Parent::transfer_quota` function.
 148            * This will result in the invokation of `Root::upgrade` at the
 149            * root interface the session was created with. The root interface,
 150            * in turn, informs the session about the new resources via the
 151            * `_upgrade_session` function. The default implementation is
 152            * suited for sessions that use a static amount of resources
 153            * accounted for at session-creation time. For such sessions, an
 154            * upgrade is not useful. However, sessions that dynamically
 155            * allocate resources on behalf of its client, should respond to
 156            * quota upgrades by implementing this function.
 157            *
 158            * \param session  session to upgrade
 159            * \param args     description of additional resources in the
 160            *                 same format as used at session creation
 161            */

 162           virtual void _upgrade_session(SESSION_TYPE *, const char *) { }

 163  
 164           virtual void _destroy_session(SESSION_TYPE *session) {
 165              destroy(_md_alloc, session); }

 166  
 167           /**
 168            * Return allocator to allocate server object in `_create_session()`
 169            */

 170           Allocator      *md_alloc() { return _md_alloc; }

 171           Rpc_entrypoint *ep()       { return _ep; }

 172  
 173        public:
 174  
 175           /**
 176            * Constructor
 177            *
 178            * \param ep           entry point that manages the sessions of this
 179            *                     root interface.
 180            * \param ram_session  provider of dataspaces for the backing store
 181            *                     of session objects and session data
 182            */

 183           Root_component(Rpc_entrypoint *ep, Allocator *metadata_alloc)
 184           : _ep(ep), _md_alloc(metadata_alloc) { }

 185  
 186  
 187           /********************
 188            ** Root interface **
 189            ********************/

 190  
 191           Session_capability session(Root::Session_args const &args,
 192                                      Affinity           const &affinity)

 193           {
 194              if (!args.is_valid_string()) throw Root::Invalid_args();
 195  
 196              POLICY::aquire(args.string());
 197  
 198              /*
 199               * We need to decrease `ram_quota` by
 200               * the size of the session object.
 201               */

 202              size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").long_value(0);
 203              size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE));
 204  
 205              if (needed > ram_quota) {
 206                 PERR("Insufficient ram quota, provided=%zu, required=%zu",
 207                      ram_quota, needed)
;

 208                 throw Root::Quota_exceeded();

 209              }

 210  
 211              size_t const remaining_ram_quota = ram_quota - needed;

 212  
 213              /*
 214               * Deduce ram quota needed for allocating the session object from the
 215               * donated ram quota.
 216               *
 217               * XXX  the size of the `adjusted_args` buffer should dependent
 218               *      on the message-buffer size and stack size.
 219               */

 220              enum { MAX_ARGS_LEN = 256 };

 221              char adjusted_args[MAX_ARGS_LEN];
 222              strncpy(adjusted_args, args.string(), sizeof(adjusted_args));
 223              char ram_quota_buf[64];
 224              snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%zu",
 225                       remaining_ram_quota)
;

 226              Arg_string::set_arg(adjusted_args, sizeof(adjusted_args),
 227                                  "ram_quota", ram_quota_buf)
;

 228  
 229              SESSION_TYPE *= 0;
 230              try { = _create_session(adjusted_args, affinity); }
 231              catch (Allocator::Out_of_memory) { throw Root::Quota_exceeded(); }

 232  
 233              return _ep->manage(s);

 234           }

 235  
 236           void upgrade(Session_capability session, Root::Upgrade_args const &args)
 237           {
 238              if (!args.is_valid_string()) throw Root::Invalid_args();
 239  
 240              typedef typename Object_pool<SESSION_TYPE>::Guard Object_guard;
 241              Object_guard s(_ep->lookup_and_lock(session));
 242              if (!s) return;
 243  
 244              _upgrade_session(s, args.string());

 245           }

 246  
 247           void close(Session_capability session)
 248           {
 249              SESSION_TYPE * s =
 250                 dynamic_cast<SESSION_TYPE *>(_ep->lookup_and_lock(session));

 251              if (!s) return;
 252  
 253              /* let the entry point forget the session object */
 254              _ep->dissolve(s);
 255  
 256              _destroy_session(s);
 257  
 258              POLICY::release();
 259              return;

 260           }

 261     }
;

 262  }

 263  
 264  #endif /* _INCLUDE__ROOT__COMPONENT_H_ */