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;
33 * Acting instance for a thread group
35 * A replicated thread does not possess a unique capability through which it is
36 * accessible. However, Fiasco allows others to send a message to a thread
37 * using its thread capability directly instead of an IPC gate. In order to
38 * allow for this in the context of replicated thread groups, we replace the
39 * original cap slot with an IPC gate that receives all these messages. A
40 * dedicated thread, the GateAgent, is attached to this gate. It waits for
41 * messages and delivers them to the replicas.
43 * Q: Does the GateAgent receive early / out-of-order messages?
44 * A: No. The GateAgent only performs an operation if the replicated thread group
45 * decided to do so. It then only acts as their agent.
47 * Q: Do we need to distinguish external send and call operations?
48 * A: No. A reply will only be sent if the thread group decides so. The gateagent will
49 * use the proper function required in every case.
51 * Q: IPC wait() does not specify a gate to wait on. How do we handle this?
52 * A: We intercept all calls that bind the thread to a newly created gate. In
53 * every case, instead of binding a real thread, we bind the GateAgent to this
54 * gate. Thereby, an open wait is triggered by the thread group, but always
55 * carried out by the agent and therefore all messages arrive in the right
58 * Q: How do we handle IRQs / explicit receive gates?
59 * A: The gate agent also handles those. To do so, it needs to be attached to these
60 * objects (as opposed to the original threads). Therefore, we need to intercept
61 * IPC gate creation (see the factory syscall handler) and IRQ object attachment
62 * (see syscall handling) and adapt their parameters to use the thread group's
71 enum { GateAgent_label = 0x195300 };
73 sem_t init_sem; // semaphore for startup
74 L4::Cap<L4::Kobject> gate_cap; // IPC gate the agent polls on
76 L4::Cap<L4::Irq> gate_irq; // IRQ to notify the agent of pending work
78 pthread_t listener; // gate agent thread
79 L4::Cap<L4::Thread> listener_cap; // gate agent thread cap
81 Romain::Thread_group* owner_group; // thread group this agent belongs to
82 Romain::App_thread* current_client; // currently handled replica thread (will be
83 // one of the group's threads)
85 static void *listener_function(void *gk);
88 * Trigger gate agent (replica -> agent)
90 void trigger_agent(Romain::App_thread *t)
92 _check(current_client != 0, "current client not 0");
95 while (current_client)
102 l4_ipc_wait(l4_utcb(), &lbl, L4_IPC_NEVER);
103 /* No error check. After return from this call, the UTCB contains
104 * the IPC error the gate agent received, which may in fact be a real
105 * IPC error -> it is up to the app to check and react on it. */
108 DEBUG() << "agent returned.";
112 GateAgent(unsigned cap_idx, Romain::Thread_group *tg)
113 : owner_group(tg), current_client(0)
115 DEBUG() << "\033[31;1mGateAgent\033[0m";
116 sem_init(&init_sem, 0, 0);
119 * launch listener thread
121 int err = pthread_create(&listener, 0,
122 listener_function, (void*)this);
123 _check(err != 0, "error creating listener thread");
125 listener_cap = L4::Cap<L4::Thread>(pthread_getl4cap(listener));
126 _check(!listener_cap.is_valid(), "could not get listener pthread cap?");
131 gate_cap = L4::Cap<L4::Kobject>(cap_idx << L4_CAP_SHIFT);
132 _check(!gate_cap.is_valid(), "could not create gate");
133 l4_msgtag_t tag = L4Re::Env::env()->factory()->create_gate(gate_cap,
137 enter_kdebug("gate creation error");
142 * Create notification IRQ
144 * Q: Why no pthread semaphore or mutex?
145 * A: Evil things happened. Seems we disrupt some pthreads assumptions
146 * when doing so in our context.
148 gate_irq = L4Re::Util::cap_alloc.alloc<L4::Irq>();
149 //_check(!gate_irq.valid(), "error allocating gate2");
150 tag = L4Re::Env::env()->factory()->create_irq(gate_irq);
152 enter_kdebug("IRQ creation error");
154 tag = gate_irq->attach(GateAgent_label+2, listener_cap);
156 enter_kdebug("attach error");
161 DEBUG() << "started agent thread";
166 * Instance of a replicated thread
168 * In contrast to an App_instance (which maps to a dedicated address space),
169 * a thread group simply keeps track of which App_threads belong together
170 * as they run the same original thread code.
174 Thread_group(std::string n, unsigned cap_idx, unsigned u)
175 : threads(), name(n), uid(u), stopped(true)
177 DEBUG() << "\033[31;1mThread_group\033[0m " << std::hex << cap_idx;
179 gateagent = new GateAgent(cap_idx, this);
181 sem_init(&activation_sem, 0, 0);
184 /* Replica threads */
185 std::vector<Romain::App_thread*> threads;
187 /* DBG: group name, unique ID */
191 /* The group's gate agent. Docs, see there. */
192 GateAgent* gateagent;
195 * Marks the thread group as currently stopped.
197 * - initially, we start in this state, because threads are created
198 * using factory_create and later run when activated through the
200 * - later we track stopped states, e.g., when blocking in a system
206 * Barrier that is used to block vCPU until the thread gets
209 sem_t activation_sem;
212 Romain::RedundancyCallback *redundancyCB;
213 void set_redundancy_callback(Romain::RedundancyCallback* cb)
214 { redundancyCB = cb; }
217 void add_replica(App_thread *a)
219 threads.push_back(a);
224 * Activate the thread group
226 * notify all vCPUs blocking on the activation semaphore
232 for (unsigned i = 0; i < threads.size(); ++i) {
233 sem_post(&activation_sem);
239 * Lets the current thread indicate being ready to run
240 * by waiting on the activation semapore.
244 sem_wait(&activation_sem);
248 Romain::App_thread* get(unsigned number)
250 _check(number < threads.size(), "invalid thread instance requested");
251 return threads[number];
255 void ex_regs(Romain::App_thread *caller);
256 void scheduler_run(Romain::App_thread* caller);
259 * Check if thread_control() was called with parameters
260 * we don't support yet.
262 void sanity_check_control(unsigned flags, l4_utcb_t *utcb);
263 void control(Romain::App_thread *t, l4_utcb_t *utcb, Romain::App_model* am);
265 void gdt(Romain::App_thread *t, l4_utcb_t *utcb);