6 * Thread group -> representation of a single replicated thread
8 * (c) 2012-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
9 * economic rights: Technische Universität Dresden (Germany)
10 * This file is part of TUD:OS and distributed under the terms of the
11 * GNU General Public License 2.
12 * Please see the COPYING-GPL-2 file for details.
17 #include "app_loading"
19 #include <l4/sys/ipc.h>
22 #include <pthread-l4.h>
23 #include <semaphore.h>
28 class RedundancyCallback;
37 * Acting instance for a thread group
39 * A replicated thread does not possess a unique capability through which it is
40 * accessible. However, Fiasco allows others to send a message to a thread
41 * using its thread capability directly instead of an IPC gate. In order to
42 * allow for this in the context of replicated thread groups, we replace the
43 * original cap slot with an IPC gate that receives all these messages. A
44 * dedicated thread, the GateAgent, is attached to this gate. It waits for
45 * messages and delivers them to the replicas.
47 * Q: Does the GateAgent receive early / out-of-order messages?
48 * A: No. The GateAgent only performs an operation if the replicated thread group
49 * decided to do so. It then only acts as their agent.
51 * Q: Do we need to distinguish external send and call operations?
52 * A: No. A reply will only be sent if the thread group decides so. The gateagent will
53 * use the proper function required in every case.
55 * Q: IPC wait() does not specify a gate to wait on. How do we handle this?
56 * A: We intercept all calls that bind the thread to a newly created gate. In
57 * every case, instead of binding a real thread, we bind the GateAgent to this
58 * gate. Thereby, an open wait is triggered by the thread group, but always
59 * carried out by the agent and therefore all messages arrive in the right
62 * Q: How do we handle IRQs / explicit receive gates?
63 * A: The gate agent also handles those. To do so, it needs to be attached to these
64 * objects (as opposed to the original threads). Therefore, we need to intercept
65 * IPC gate creation (see the factory syscall handler) and IRQ object attachment
66 * (see syscall handling) and adapt their parameters to use the thread group's
75 enum { GateAgent_label = 0x195300 };
77 sem_t init_sem; // semaphore for startup
78 L4::Cap<L4::Kobject> gate_cap; // IPC gate the agent polls on
80 L4::Cap<L4::Irq> gate_irq; // IRQ to notify the agent of pending work
82 pthread_t listener; // gate agent thread
83 L4::Cap<L4::Thread> listener_cap; // gate agent thread cap
85 Romain::Thread_group* owner_group; // thread group this agent belongs to
86 Romain::App_thread* current_client; // currently handled replica thread (will be
87 // one of the group's threads)
89 static void *listener_function(void *gk);
92 * Trigger gate agent (replica -> agent)
94 void trigger_agent(Romain::App_thread *t)
96 _check(current_client != 0, "current client not 0");
99 while (current_client)
106 l4_ipc_wait(l4_utcb(), &lbl, L4_IPC_NEVER);
107 /* No error check. After return from this call, the UTCB contains
108 * the IPC error the gate agent received, which may in fact be a real
109 * IPC error -> it is up to the app to check and react on it. */
112 DEBUG() << "agent returned.";
116 GateAgent(l4_umword_t cap_idx, Romain::Thread_group *tg)
117 : owner_group(tg), current_client(0)
119 DEBUG() << BOLD_RED << "GateAgent" << NOCOLOR;
120 sem_init(&init_sem, 0, 0);
123 * launch listener thread
125 l4_mword_t err = pthread_create(&listener, 0,
126 listener_function, (void*)this);
127 _check(err != 0, "error creating listener thread");
130 listener_cap = L4::Cap<L4::Thread>(pthread_getl4cap(listener));
131 _check(!listener_cap.is_valid(), "could not get listener pthread cap?");
136 gate_cap = L4::Cap<L4::Kobject>(cap_idx << L4_CAP_SHIFT);
137 _check(!gate_cap.is_valid(), "could not create gate");
138 l4_msgtag_t tag = L4Re::Env::env()->factory()->create_gate(gate_cap,
142 enter_kdebug("gate creation error");
147 * Create notification IRQ
149 * Q: Why no pthread semaphore or mutex?
150 * A: Evil things happened. Seems we disrupt some pthreads assumptions
151 * when doing so in our context.
153 gate_irq = L4Re::Util::cap_alloc.alloc<L4::Irq>();
154 //_check(!gate_irq.valid(), "error allocating gate2");
155 tag = L4Re::Env::env()->factory()->create_irq(gate_irq);
157 enter_kdebug("IRQ creation error");
159 tag = gate_irq->attach(GateAgent_label+2, listener_cap);
161 enter_kdebug("attach error");
166 DEBUG() << "started agent thread";
171 * Instance of a replicated thread
173 * In contrast to an App_instance (which maps to a dedicated address space),
174 * a thread group simply keeps track of which App_threads belong together
175 * as they run the same original thread code.
179 Thread_group(std::string n, l4_umword_t cap_idx, l4_umword_t u)
180 : threads(), name(n), uid(u), stopped(true)
182 DEBUG() << BOLD_RED << "Thread_group " << NOCOLOR << std::hex << cap_idx;
184 gateagent = new GateAgent(cap_idx, this);
186 sem_init(&activation_sem, 0, 0);
189 /* Replica threads */
190 std::vector<Romain::App_thread*> threads;
192 /* DBG: group name, unique ID */
196 /* The group's gate agent. Docs, see there. */
197 GateAgent* gateagent;
200 * Marks the thread group as currently stopped.
202 * - initially, we start in this state, because threads are created
203 * using factory_create and later run when activated through the
205 * - later we track stopped states, e.g., when blocking in a system
211 * Barrier that is used to block vCPU until the thread gets
214 sem_t activation_sem;
217 Romain::RedundancyCallback *redundancyCB;
218 void set_redundancy_callback(Romain::RedundancyCallback* cb)
219 { redundancyCB = cb; }
222 Romain::Watchdog *watchdog;
223 void set_watchdog(Romain::Watchdog *w)
227 void add_replica(App_thread *a)
229 threads.push_back(a);
233 /* Halt all threads by setting their prio to 0 */
236 INFO() << "Halting TG '" << name << "'";
237 for (auto it = threads.begin(); it != threads.end(); ++it) {
244 * Activate the thread group
246 * notify all vCPUs blocking on the activation semaphore
252 for (l4_umword_t i = 0; i < threads.size(); ++i) {
253 sem_post(&activation_sem);
259 * Lets the current thread indicate being ready to run
260 * by waiting on the activation semapore.
264 sem_wait(&activation_sem);
268 Romain::App_thread* get(l4_umword_t number)
270 _check(number < threads.size(), "invalid thread instance requested");
271 return threads[number];
275 void ex_regs(Romain::App_thread *caller);
276 void scheduler_run(Romain::App_thread* caller);
279 * Check if thread_control() was called with parameters
280 * we don't support yet.
282 void sanity_check_control(l4_umword_t flags, l4_utcb_t *utcb);
283 void control(Romain::App_thread *t, l4_utcb_t *utcb, Romain::App_model* am);
285 void gdt(Romain::App_thread *t, l4_utcb_t *utcb);