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 *s = 0;
230 try { s = _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_ */