]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ipc_gate.cpp
5dc2dc87247ba1e5be131639465cd18d2d9be563
[l4.git] / kernel / fiasco / src / kern / ipc_gate.cpp
1 INTERFACE:
2
3 #include "kobject.h"
4 #include "kobject_helper.h"
5 #include "ref_ptr.h"
6 #include "slab_cache_anon.h"
7 #include "thread_object.h"
8
9 class Ram_quota;
10
11 class Ipc_gate_obj;
12
13 class Ipc_gate_ctl : public Kobject_h<Ipc_gate_ctl, Kobject_iface>
14 {
15 private:
16   enum Operation
17   {
18     Op_bind     = 0x10,
19     Op_get_info = 0x11,
20   };
21 };
22
23 class Ipc_gate : public Kobject
24 {
25   friend class Ipc_gate_ctl;
26 protected:
27
28   Ref_ptr<Thread> _thread;
29   Mword _id;
30   Ram_quota *_quota;
31   Locked_prio_list _wait_q;
32 };
33
34 class Ipc_gate_obj : public Ipc_gate, public Ipc_gate_ctl
35 {
36   FIASCO_DECLARE_KOBJ();
37
38 private:
39   friend class Ipc_gate;
40   typedef slab_cache_anon Self_alloc;
41
42 public:
43   bool put() { return Ipc_gate::put(); }
44
45   Thread *thread() const { return _thread.ptr(); }
46   Mword id() const { return _id; }
47   Mword obj_id() const { return _id; }
48   bool is_local(Space *s) const { return _thread->space() == s; }
49 };
50
51 //---------------------------------------------------------------------------
52 INTERFACE [debug]:
53
54 EXTENSION class Ipc_gate
55 {
56 protected:
57   struct Log_ipc_gate_invoke
58   {
59     Mword gate_dbg_id;
60     Mword thread_dbg_id;
61     Mword label;
62   };
63
64   static unsigned log_fmt(Tb_entry *, int max, char *buf) asm ("__fmt_ipc_gate_invoke");
65 };
66
67 //---------------------------------------------------------------------------
68 IMPLEMENTATION:
69
70 #include <cstddef>
71
72 #include "entry_frame.h"
73 #include "ipc_timeout.h"
74 #include "kmem_slab.h"
75 #include "logdefs.h"
76 #include "ram_quota.h"
77 #include "static_init.h"
78 #include "thread.h"
79 #include "thread_state.h"
80 #include "timer.h"
81
82 FIASCO_DEFINE_KOBJ(Ipc_gate_obj);
83
84 PUBLIC
85 ::Kobject_mappable *
86 Ipc_gate_obj::map_root()
87 { return Ipc_gate::map_root(); }
88
89 PUBLIC
90 Kobject_iface *
91 Ipc_gate_obj::downgrade(unsigned long attr)
92 {
93   if (attr & L4_fpage::C_obj_right_1)
94     return static_cast<Ipc_gate*>(this);
95   else
96     return static_cast<Ipc_gate_ctl*>(this);
97 }
98
99 PUBLIC inline
100 Ipc_gate::Ipc_gate(Ram_quota *q, Thread *t, Mword id)
101   : _thread(t), _id(id), _quota(q), _wait_q()
102 {}
103
104 PUBLIC inline
105 Ipc_gate_obj::Ipc_gate_obj(Ram_quota *q, Thread *t, Mword id)
106   : Ipc_gate(q, t, id)
107 {}
108
109 PUBLIC
110 void
111 Ipc_gate_obj::unblock_all()
112 {
113   while (::Prio_list_elem *h = _wait_q.head())
114     {
115       Lock_guard<Cpu_lock> g1(&cpu_lock);
116       Thread *w;
117         {
118           Lock_guard<typeof(_wait_q)> g2(&_wait_q);
119           if (EXPECT_FALSE(h != _wait_q.head()))
120             continue;
121
122           w = static_cast<Thread*>(Sender::cast(h));
123           w->sender_dequeue(&_wait_q);
124         }
125       w->activate();
126     }
127 }
128
129 PUBLIC virtual
130 void
131 Ipc_gate_obj::initiate_deletion(Kobject ***r)
132 {
133   if (_thread)
134     _thread->ipc_gate_deleted(_id);
135
136   Kobject::initiate_deletion(r);
137 }
138
139 PUBLIC virtual
140 void
141 Ipc_gate_obj::destroy(Kobject ***r)
142 {
143   Kobject::destroy(r);
144   _thread = 0;
145   unblock_all();
146 }
147
148 PUBLIC
149 Ipc_gate_obj::~Ipc_gate_obj()
150 {
151   unblock_all();
152 }
153
154 PUBLIC inline NEEDS[<cstddef>]
155 void *
156 Ipc_gate_obj::operator new (size_t, void *b)
157 { return b; }
158
159 static Kmem_slab_t<Ipc_gate_obj> _ipc_gate_allocator("Ipc_gate");
160
161 PRIVATE static
162 Ipc_gate_obj::Self_alloc *
163 Ipc_gate_obj::allocator()
164 { return &_ipc_gate_allocator; }
165
166 PUBLIC static
167 Ipc_gate_obj *
168 Ipc_gate::create(Ram_quota *q, Thread *t, Mword id)
169 {
170   void *nq;
171   if (q->alloc(sizeof(Ipc_gate_obj)))
172     {
173       if (nq = Ipc_gate_obj::allocator()->alloc())
174         return new (nq) Ipc_gate_obj(q, t, id);
175       else
176         q->free(sizeof(Ipc_gate_obj));
177     }
178
179   return 0;
180 }
181
182 PUBLIC
183 void Ipc_gate_obj::operator delete (void *_f)
184 {
185   register Ipc_gate_obj *f = (Ipc_gate_obj*)_f;
186   Ram_quota *p = f->_quota;
187
188   if (p)
189     p->free(sizeof(Ipc_gate_obj));
190
191   allocator()->free(f);
192 }
193
194 PRIVATE inline NOEXPORT
195 L4_msg_tag
196 Ipc_gate_ctl::bind_thread(L4_obj_ref, Mword, Syscall_frame *f, Utcb const *in, Utcb *)
197 {
198   L4_msg_tag tag = f->tag();
199   L4_snd_item_iter snd_items(in, tag.words());
200
201   if (tag.words() < 2 || !tag.items() || !snd_items.next())
202     return commit_result(-L4_err::EInval);
203
204   L4_fpage bind_thread(snd_items.get()->d);
205   if (EXPECT_FALSE(!bind_thread.is_objpage()))
206     return commit_error(in, L4_error::Overflow);
207
208   register Context *const c_thread = ::current();
209   register Space *const c_space = c_thread->space();
210   register Obj_space *const o_space = c_space->obj_space();
211   unsigned char t_rights = 0;
212   Thread *t = Kobject::dcast<Thread_object*>(o_space->lookup_local(bind_thread.obj_index(), &t_rights));
213
214   if (!(t_rights & L4_fpage::CS))
215     return commit_result(-L4_err::EPerm);
216
217
218   Ipc_gate_obj *g = static_cast<Ipc_gate_obj*>(this);
219   g->_id = in->values[1];
220   Mem::mp_wmb();
221   g->_thread = t;
222   Mem::mp_wmb();
223   g->unblock_all();
224   c_thread->rcu_wait();
225   g->unblock_all();
226
227   return commit_result(0);
228 }
229
230 PRIVATE inline NOEXPORT
231 L4_msg_tag
232 Ipc_gate_ctl::get_infos(L4_obj_ref, Mword, Syscall_frame *, Utcb const *, Utcb *out)
233 {
234   Ipc_gate_obj *g = static_cast<Ipc_gate_obj*>(this);
235   out->values[0] = g->_id;
236   return commit_result(0, 1);
237 }
238
239 PUBLIC
240 void
241 Ipc_gate_ctl::invoke(L4_obj_ref self, Mword rights, Syscall_frame *f, Utcb *utcb)
242 {
243   if (f->tag().proto() == L4_msg_tag::Label_kobject)
244     Kobject_h<Ipc_gate_ctl, Kobject_iface>::invoke(self, rights, f, utcb);
245   else
246     static_cast<Ipc_gate_obj*>(this)->Ipc_gate::invoke(self, rights, f, utcb);
247 }
248
249
250 PUBLIC
251 L4_msg_tag
252 Ipc_gate_ctl::kinvoke(L4_obj_ref self, Mword rights, Syscall_frame *f, Utcb const *in, Utcb *out)
253 {
254   L4_msg_tag tag = f->tag();
255
256   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_kobject))
257     return commit_result(-L4_err::EBadproto);
258
259   if (EXPECT_FALSE(tag.words() < 1))
260     return commit_result(-L4_err::EInval);
261
262   switch (in->values[0])
263     {
264     case Op_bind:
265       return bind_thread(self, rights, f, in, out);
266     case Op_get_info:
267       return get_infos(self, rights, f, in, out);
268     default:
269       return static_cast<Ipc_gate_obj*>(this)->kobject_invoke(self, rights, f, in, out);
270     }
271 }
272
273 PRIVATE inline NOEXPORT
274 L4_error
275 Ipc_gate::block(Thread *ct, L4_timeout const &to, Utcb *u)
276 {
277   Unsigned64 t = 0;
278   if (!to.is_never())
279     {
280       t = to.microsecs(Timer::system_clock(), u);
281       if (!t)
282         return L4_error::Timeout;
283     }
284
285     {
286       Lock_guard<typeof(_wait_q)> g(&_wait_q);
287       ct->wait_queue(&_wait_q);
288       ct->sender_enqueue(&_wait_q, ct->sched_context()->prio());
289     }
290   ct->state_change_dirty(~Thread_ready, Thread_ipc_in_progress | Thread_send_in_progress);
291
292   IPC_timeout timeout;
293   if (t)
294     {
295       timeout.set(t, ct->cpu());
296       ct->set_timeout(&timeout);
297     }
298
299   ct->schedule();
300
301   ct->state_change(~(Thread_ipc_in_progress | Thread_send_in_progress), Thread_ready);
302   ct->reset_timeout();
303
304   if (EXPECT_FALSE(ct->in_sender_list() && timeout.has_hit()))
305     {
306       Lock_guard<typeof(_wait_q)> g(&_wait_q);
307       if (!ct->in_sender_list())
308         return L4_error::None;
309
310       ct->sender_dequeue(&_wait_q);
311       return L4_error::Timeout;
312     }
313   return L4_error::None;
314 }
315
316
317 PUBLIC
318 void
319 Ipc_gate::invoke(L4_obj_ref /*self*/, Mword rights, Syscall_frame *f, Utcb *utcb)
320 {
321   Syscall_frame *ipc_f = f;
322   //LOG_MSG_3VAL(current(), "gIPC", Mword(_thread), _id, f->obj_2_flags());
323   //printf("Invoke: Ipc_gate(%lx->%p)...\n", _id, _thread);
324   Thread *ct = current_thread();
325   Thread *sender = 0;
326   Thread *partner = 0;
327   bool have_rcv = false;
328
329   if (EXPECT_FALSE(!_thread.ptr()))
330     {
331       L4_error e = block(ct, f->timeout().snd, utcb);
332       if (!e.ok())
333         {
334           f->tag(commit_error(utcb, e));
335           return;
336         }
337
338       if (EXPECT_FALSE(!_thread.ptr()))
339         {
340           f->tag(commit_error(utcb, L4_error::Not_existent));
341           return;
342         }
343     }
344
345   bool ipc = _thread->check_sys_ipc(f->ref().flags(), &partner, &sender, &have_rcv);
346
347   LOG_TRACE("IPC Gate invoke", "gate", current(), __fmt_ipc_gate_invoke,
348       Log_ipc_gate_invoke *l = tbe->payload<Log_ipc_gate_invoke>();
349       l->gate_dbg_id = dbg_id();
350       l->thread_dbg_id = _thread->dbg_id();
351       l->label = _id | rights;
352   );
353
354   if (EXPECT_FALSE(!ipc))
355     f->tag(commit_error(utcb, L4_error::Not_existent));
356   else
357     {
358       ipc_f->from(_id | rights);
359       ct->do_ipc(f->tag(), partner, partner, have_rcv, sender,
360                  f->timeout(), f, rights);
361     }
362 }
363
364 //---------------------------------------------------------------------------
365 IMPLEMENTATION [debug]:
366
367 PUBLIC
368 ::Kobject_dbg *
369 Ipc_gate_obj::dbg_info() const
370 { return Ipc_gate::dbg_info(); }
371
372 IMPLEMENT
373 unsigned
374 Ipc_gate::log_fmt(Tb_entry *e, int max, char *buf)
375 {
376   Log_ipc_gate_invoke *l = e->payload<Log_ipc_gate_invoke>();
377   return snprintf(buf, max, "D-gate=%lx D-thread=%lx L=%lx",
378                   l->gate_dbg_id, l->thread_dbg_id, l->label);
379 }
380