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