]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ipc_sender.cpp
update
[l4.git] / kernel / fiasco / src / kern / ipc_sender.cpp
1 INTERFACE:
2
3 #include "sender.h"
4 #include "receiver.h"
5
6 class Ipc_sender_base : public Sender
7 {
8 };
9
10 template< typename Derived >
11 class Ipc_sender : public Ipc_sender_base
12 {
13 private:
14   Derived *derived() { return static_cast<Derived*>(this); }
15   static bool dequeue_sender() { return true; }
16   static bool requeue_sender() { return false; }
17 };
18
19 extern "C" void fast_ret_from_irq(void);
20
21 IMPLEMENTATION:
22
23 #include "config.h"
24 #include "entry_frame.h"
25 #include "globals.h"
26 #include "kdb_ke.h"
27 #include "thread_state.h"
28 #include <cassert>
29
30 PUBLIC
31 virtual void
32 Ipc_sender_base::ipc_receiver_aborted()
33 {
34   assert (receiver());
35
36   sender_dequeue(receiver()->sender_list());
37   receiver()->vcpu_update_state();
38   set_receiver(0);
39 }
40
41 /** Sender-activation function called when receiver gets ready.
42     Irq::hit() actually ensures that this method is always called
43     when an interrupt occurs, even when the receiver was already
44     waiting.
45  */
46 PUBLIC template< typename Derived >
47 virtual void
48 Ipc_sender<Derived>::ipc_send_msg(Receiver *recv)
49 {
50   derived()->transfer_msg(recv);
51   if (derived()->dequeue_sender())
52     {
53       sender_dequeue(recv->sender_list());
54       recv->vcpu_update_state();
55     }
56 }
57
58 PROTECTED inline NEEDS["config.h", "globals.h", "thread_state.h"]
59 bool
60 Ipc_sender_base::handle_shortcut(Syscall_frame *dst_regs,
61                                  Receiver *receiver)
62 {
63   if (EXPECT_TRUE
64       ((current() != receiver
65         && receiver->sched()->deblock(current_cpu(), current()->sched(), true)
66         // avoid race in do_ipc() after Thread_send_in_progress
67         // flag was deleted from receiver's thread state
68         // also: no shortcut for alien threads, they need to see the
69         // after-syscall exception
70         && !(receiver->state()
71           & (Thread_ready_mask | Thread_alien))
72         && !current()->schedule_in_progress()))) // no schedule in progress
73     {
74       // we don't need to manipulate the state in a safe way
75       // because we are still running with interrupts turned off
76       receiver->state_add_dirty(Thread_ready);
77
78       if (!Config::Irq_shortcut)
79         {
80           // no shortcut: switch to the interrupt thread which will
81           // calls Irq::ipc_receiver_ready
82           current()->switch_to_locked(receiver);
83           return true;
84         }
85
86       // The following shortcut optimization does not work if PROFILE
87       // is defined because fast_ret_from_irq does not handle the
88       // different implementation of the kernel lock in profiling mode
89
90       // At this point we are sure that the connected interrupt
91       // thread is waiting for the next interrupt and that its 
92       // thread priority is higher than the current one. So we
93       // choose a short cut: Instead of doing the full ipc handshake
94       // we simply build up the return stack frame and go out as 
95       // quick as possible.
96       //
97       // XXX We must own the kernel lock for this optimization!
98       //
99
100       Mword *esp = reinterpret_cast<Mword*>(dst_regs);
101
102       // set return address of irq_thread
103       *--esp = reinterpret_cast<Mword>(fast_ret_from_irq);
104
105       // XXX set stack pointer of irq_thread
106       receiver->set_kernel_sp(esp);
107
108       // directly switch to the interrupt thread context and go out
109       // fast using fast_ret_from_irq (implemented in assembler).
110       // kernel-unlock is done in switch_exec() (on switchee's side).
111
112       // no shortcut if profiling: switch to the interrupt thread
113       current()->switch_to_locked (receiver);
114       return true;
115     }
116   return false;
117 }
118
119
120 PROTECTED template< typename Derived >
121 inline  NEEDS["config.h","globals.h", "thread_state.h",
122               Ipc_sender_base::handle_shortcut]
123 void
124 Ipc_sender<Derived>::send_msg(Receiver *receiver)
125 {
126   set_receiver(receiver);
127
128   if (!Config::Irq_shortcut)
129     {
130       // in profile mode, don't optimize
131       // in non-profile mode, enqueue _after_ shortcut if still necessary
132       sender_enqueue(receiver->sender_list(), 255);
133       receiver->vcpu_set_irq_pending();
134     }
135
136   // if the thread is waiting for this interrupt, make it ready;
137   // this will cause it to run irq->receiver_ready(), which
138   // handles the rest
139
140   // XXX careful!  This code may run in midst of an do_ipc()
141   // operation (or similar)!
142   if (Receiver::Rcv_state s = receiver->sender_ok(this))
143     {
144       Syscall_frame *dst_regs = derived()->transfer_msg(receiver);
145
146       if (derived()->requeue_sender())
147         {
148           sender_enqueue(receiver->sender_list(), 255);
149           receiver->vcpu_set_irq_pending();
150         }
151
152       // ipc completed
153       receiver->state_change_dirty(~Thread_ipc_mask, 0);
154
155       // in case a timeout was set
156       receiver->reset_timeout();
157
158       if (s == Receiver::Rs_ipc_receive)
159         {
160           if (handle_shortcut(dst_regs, receiver))
161             return;
162         }
163       // we don't need to manipulate the state in a safe way
164       // because we are still running with interrupts turned off
165       receiver->state_add_dirty(Thread_ready);
166       receiver->sched()->deblock(receiver->cpu());
167       return;
168     }
169
170   if (Config::Irq_shortcut)
171     {
172       // in profile mode, don't optimize
173       // in non-profile mode, enqueue after shortcut if still necessary
174       sender_enqueue(receiver->sender_list(), 255);
175       receiver->vcpu_set_irq_pending();
176     }
177 }
178