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