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     class Single_client;
  30     class Multiple_clients;
  31     template <typename, typename POLICY = Multiple_clients> class Root_component;
  32  }

  33  
  34  
  35  /**
  36   * Session creation policy for a single-client service
  37   */

  38  class Genode::Single_client
  39  {
  40     private:
  41  
  42        bool _used;

  43  
  44     public:
  45  
  46        Single_client() : _used(0) { }
  47  
  48        void aquire(const char *)
  49        {
  50           if (_used)
  51              throw Root::Unavailable();

  52  
  53           _used = true;

  54        }

  55  
  56        void release() { _used = false; }

  57  }
;

  58  
  59  
  60  /**
  61   * Session-creation policy for a multi-client service
  62   */

  63  struct Genode::Multiple_clients
  64  {
  65     void aquire(const char *) { }
  66     void release() { }

  67  }
;

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

  96  template <typename SESSION_TYPE, typename POLICY>
  97  class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
  98                                 private POLICY

  99  {
 100     private:
 101  
 102        /*
 103         * Entry point that manages the session objects
 104         * created by this root interface
 105         */

 106        Rpc_entrypoint *_ep;
 107  
 108        /*
 109         * Allocator for allocating session objects.
 110         * This allocator must be used by the derived
 111         * class when calling the `new` operator for
 112         * creating a new session.
 113         */

 114        Allocator *_md_alloc;

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

 139        virtual SESSION_TYPE *_create_session(const char *args,
 140                                              Affinity const &)

 141        {
 142           return _create_session(args);
 143        }

 144  
 145        virtual SESSION_TYPE *_create_session(const char *args)
 146        {
 147           throw Root::Invalid_args();
 148        }

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

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

 170  
 171        virtual void _destroy_session(SESSION_TYPE *session) {
 172           destroy(_md_alloc, session); }

 173  
 174        /**
 175         * Return allocator to allocate server object in `_create_session()`
 176         */

 177        Allocator *md_alloc() { return _md_alloc; }

 178  
 179        /**
 180         * Return entrypoint that serves the root component
 181         */

 182        Rpc_entrypoint *ep() { return _ep; }

 183  
 184     public:
 185  
 186        /**
 187         * Constructor
 188         *
 189         * \param ep           entry point that manages the sessions of this
 190         *                     root interface.
 191         * \param ram_session  provider of dataspaces for the backing store
 192         *                     of session objects and session data
 193         */

 194        Root_component(Rpc_entrypoint *ep, Allocator *metadata_alloc)
 195        : _ep(ep), _md_alloc(metadata_alloc) { }

 196  
 197  
 198        /********************
 199         ** Root interface **
 200         ********************/

 201  
 202        Session_capability session(Root::Session_args const &args,
 203                                   Affinity           const &affinity)
 override
 204        {
 205           if (!args.is_valid_string()) throw Root::Invalid_args();
 206  
 207           POLICY::aquire(args.string());
 208  
 209           /*
 210            * We need to decrease `ram_quota` by
 211            * the size of the session object.
 212            */

 213           size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
 214           size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE));
 215  
 216           if (needed > ram_quota) {
 217              PERR("Insufficient ram quota, provided=%zu, required=%zu",
 218                   ram_quota, needed)
;

 219              throw Root::Quota_exceeded();

 220           }

 221  
 222           size_t const remaining_ram_quota = ram_quota - needed;
 223  
 224           /*
 225            * Deduce ram quota needed for allocating the session object from the
 226            * donated ram quota.
 227            *
 228            * XXX  the size of the `adjusted_args` buffer should dependent
 229            *      on the message-buffer size and stack size.
 230            */

 231           enum { MAX_ARGS_LEN = 256 };

 232           char adjusted_args[MAX_ARGS_LEN];
 233           strncpy(adjusted_args, args.string(), sizeof(adjusted_args));
 234           char ram_quota_buf[64];
 235           snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%zu",
 236                    remaining_ram_quota)
;

 237           Arg_string::set_arg(adjusted_args, sizeof(adjusted_args),
 238                               "ram_quota", ram_quota_buf)
;

 239  
 240           SESSION_TYPE *= 0;
 241           try { = _create_session(adjusted_args, affinity); }
 242           catch (Allocator::Out_of_memory) { throw Root::Quota_exceeded(); }

 243  
 244           return _ep->manage(s);

 245        }

 246  
 247        void upgrade(Session_capability session, Root::Upgrade_args const &args) override
 248        {
 249           if (!args.is_valid_string()) throw Root::Invalid_args();
 250  
 251           _ep->apply(session, [&(SESSION_TYPE *s) {
 252              if (!s) return;
 253  
 254              _upgrade_session(s, args.string());

 255           }
)
;

 256        }

 257  
 258        void close(Session_capability session_cap) override
 259        {
 260           SESSION_TYPE * session;
 261  
 262           _ep->apply(session_cap, [&(SESSION_TYPE *s) {
 263              session = s;
 264  
 265              /* let the entry point forget the session object */
 266              if (session) _ep->dissolve(session);

 267           }
)
;

 268  
 269           if (!session) return;
 270  
 271           _destroy_session(session);
 272  
 273           POLICY::release();

 274        }

 275  }
;

 276  
 277  #endif /* _INCLUDE__ROOT__COMPONENT_H_ */