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