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