source: mainline/uspace/lib/c/generic/fibril.c@ e86a617a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e86a617a was c170438, checked in by Jakub Jermar <jakub@…>, 9 years ago

Do not create a new fibril for each IRQ notification

In the absence of fibril serialization, manager fibrils can
theoretically block in async IPC or on fibril synchronization
primitives. Consequently, it is safe to execute the IRQ handler directly
from the manager fibril. The manager fibril can block while processing
the notification, but most of the times it will not block and the
handler will execute atomically.

This changeset modifies the current behaviour so that we no longer spawn
a new notification fibril for each IRQ, but rather execute the handler
directly from the manager fibril and test if the execution blocked. If
it blocked, the manager fibril had assumed the role of a notification
fibril and we destroy it afterwards - merely to avoid fibril population
explosion. Otherwise, which is the usual behavior, we keep it so that
it resumes its job of a manager fibril.

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * Copyright (c) 2007 Jakub Jermar
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libc
31 * @{
32 */
33/** @file
34 */
35
36#include <adt/list.h>
37#include <fibril.h>
38#include <thread.h>
39#include <stack.h>
40#include <tls.h>
41#include <malloc.h>
42#include <abi/mm/as.h>
43#include <as.h>
44#include <unistd.h>
45#include <stdio.h>
46#include <libarch/barrier.h>
47#include <libarch/faddr.h>
48#include <futex.h>
49#include <assert.h>
50#include <async.h>
51
52#ifdef FUTEX_UPGRADABLE
53#include <rcu.h>
54#endif
55
56/**
57 * This futex serializes access to ready_list,
58 * manager_list and fibril_list.
59 */
60static futex_t fibril_futex = FUTEX_INITIALIZER;
61
62static LIST_INITIALIZE(ready_list);
63static LIST_INITIALIZE(manager_list);
64static LIST_INITIALIZE(fibril_list);
65
66/** Function that spans the whole life-cycle of a fibril.
67 *
68 * Each fibril begins execution in this function. Then the function implementing
69 * the fibril logic is called. After its return, the return value is saved.
70 * The fibril then switches to another fibril, which cleans up after it.
71 *
72 */
73static void fibril_main(void)
74{
75 fibril_t *fibril = __tcb_get()->fibril_data;
76
77#ifdef FUTEX_UPGRADABLE
78 rcu_register_fibril();
79#endif
80
81 /* Call the implementing function. */
82 fibril->retval = fibril->func(fibril->arg);
83
84 futex_down(&async_futex);
85 fibril_switch(FIBRIL_FROM_DEAD);
86 /* Not reached */
87}
88
89/** Setup fibril information into TCB structure
90 *
91 */
92fibril_t *fibril_setup(void)
93{
94 tcb_t *tcb = tls_make();
95 if (!tcb)
96 return NULL;
97
98 fibril_t *fibril = malloc(sizeof(fibril_t));
99 if (!fibril) {
100 tls_free(tcb);
101 return NULL;
102 }
103
104 tcb->fibril_data = fibril;
105 fibril->tcb = tcb;
106
107 fibril->func = NULL;
108 fibril->arg = NULL;
109 fibril->stack = NULL;
110 fibril->clean_after_me = NULL;
111 fibril->retval = 0;
112 fibril->flags = 0;
113
114 fibril->waits_for = NULL;
115
116 fibril->switches = 0;
117
118 /*
119 * We are called before __tcb_set(), so we need to use
120 * futex_down/up() instead of futex_lock/unlock() that
121 * may attempt to access TLS.
122 */
123 futex_down(&fibril_futex);
124 list_append(&fibril->all_link, &fibril_list);
125 futex_up(&fibril_futex);
126
127 return fibril;
128}
129
130void fibril_teardown(fibril_t *fibril, bool locked)
131{
132 if (!locked)
133 futex_lock(&fibril_futex);
134 list_remove(&fibril->all_link);
135 if (!locked)
136 futex_unlock(&fibril_futex);
137 tls_free(fibril->tcb);
138 free(fibril);
139}
140
141/** Switch from the current fibril.
142 *
143 * If stype is FIBRIL_TO_MANAGER or FIBRIL_FROM_DEAD, the async_futex must
144 * be held.
145 *
146 * @param stype Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
147 * FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
148 * describes the circumstances of the switch.
149 *
150 * @return 0 if there is no ready fibril,
151 * @return 1 otherwise.
152 *
153 */
154int fibril_switch(fibril_switch_type_t stype)
155{
156 futex_lock(&fibril_futex);
157
158 switch (stype) {
159 case FIBRIL_PREEMPT:
160 case FIBRIL_FROM_MANAGER:
161 if (list_empty(&ready_list)) {
162 futex_unlock(&fibril_futex);
163 return 0;
164 }
165 break;
166 case FIBRIL_TO_MANAGER:
167 case FIBRIL_FROM_DEAD:
168 /* Make sure the async_futex is held. */
169 assert((atomic_signed_t) async_futex.val.count <= 0);
170
171 /* If we are going to manager and none exists, create it */
172 while (list_empty(&manager_list)) {
173 futex_unlock(&fibril_futex);
174 async_create_manager();
175 futex_lock(&fibril_futex);
176 }
177 break;
178 }
179
180 fibril_t *srcf = __tcb_get()->fibril_data;
181 if (stype != FIBRIL_FROM_DEAD) {
182
183 /* Save current state */
184 if (!context_save(&srcf->ctx)) {
185 if (srcf->clean_after_me) {
186 /*
187 * Cleanup after the dead fibril from which we
188 * restored context here.
189 */
190 void *stack = srcf->clean_after_me->stack;
191 if (stack) {
192 /*
193 * This check is necessary because a
194 * thread could have exited like a
195 * normal fibril using the
196 * FIBRIL_FROM_DEAD switch type. In that
197 * case, its fibril will not have the
198 * stack member filled.
199 */
200 as_area_destroy(stack);
201 }
202 fibril_teardown(srcf->clean_after_me, true);
203 srcf->clean_after_me = NULL;
204 }
205
206 return 1; /* futex_unlock already done here */
207 }
208
209 /* Put the current fibril into the correct run list */
210 switch (stype) {
211 case FIBRIL_PREEMPT:
212 list_append(&srcf->link, &ready_list);
213 break;
214 case FIBRIL_FROM_MANAGER:
215 list_append(&srcf->link, &manager_list);
216 break;
217 default:
218 assert(stype == FIBRIL_TO_MANAGER);
219
220 srcf->switches++;
221
222 /*
223 * Don't put the current fibril into any list, it should
224 * already be somewhere, or it will be lost.
225 */
226 break;
227 }
228 }
229
230 fibril_t *dstf;
231
232 /* Choose a new fibril to run */
233 switch (stype) {
234 case FIBRIL_TO_MANAGER:
235 case FIBRIL_FROM_DEAD:
236 dstf = list_get_instance(list_first(&manager_list), fibril_t,
237 link);
238
239 if (stype == FIBRIL_FROM_DEAD)
240 dstf->clean_after_me = srcf;
241 break;
242 default:
243 dstf = list_get_instance(list_first(&ready_list), fibril_t,
244 link);
245 break;
246 }
247
248 list_remove(&dstf->link);
249
250 futex_unlock(&fibril_futex);
251
252#ifdef FUTEX_UPGRADABLE
253 if (stype == FIBRIL_FROM_DEAD) {
254 rcu_deregister_fibril();
255 }
256#endif
257
258 context_restore(&dstf->ctx);
259 /* not reached */
260}
261
262/** Create a new fibril.
263 *
264 * @param func Implementing function of the new fibril.
265 * @param arg Argument to pass to func.
266 * @param stksz Stack size in bytes.
267 *
268 * @return 0 on failure or TLS of the new fibril.
269 *
270 */
271fid_t fibril_create_generic(int (*func)(void *), void *arg, size_t stksz)
272{
273 fibril_t *fibril;
274
275 fibril = fibril_setup();
276 if (fibril == NULL)
277 return 0;
278
279 size_t stack_size = (stksz == FIBRIL_DFLT_STK_SIZE) ?
280 stack_size_get() : stksz;
281 fibril->stack = as_area_create((void *) -1, stack_size,
282 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
283 AS_AREA_LATE_RESERVE);
284 if (fibril->stack == (void *) -1) {
285 fibril_teardown(fibril, false);
286 return 0;
287 }
288
289 fibril->func = func;
290 fibril->arg = arg;
291
292 context_save(&fibril->ctx);
293 context_set(&fibril->ctx, FADDR(fibril_main), fibril->stack,
294 stack_size, fibril->tcb);
295
296 return (fid_t) fibril;
297}
298
299/** Delete a fibril that has never run.
300 *
301 * Free resources of a fibril that has been created with fibril_create()
302 * but never readied using fibril_add_ready().
303 *
304 * @param fid Pointer to the fibril structure of the fibril to be
305 * added.
306 */
307void fibril_destroy(fid_t fid)
308{
309 fibril_t *fibril = (fibril_t *) fid;
310
311 as_area_destroy(fibril->stack);
312 fibril_teardown(fibril, false);
313}
314
315/** Add a fibril to the ready list.
316 *
317 * @param fid Pointer to the fibril structure of the fibril to be
318 * added.
319 *
320 */
321void fibril_add_ready(fid_t fid)
322{
323 fibril_t *fibril = (fibril_t *) fid;
324
325 futex_lock(&fibril_futex);
326 list_append(&fibril->link, &ready_list);
327 futex_unlock(&fibril_futex);
328}
329
330/** Add a fibril to the manager list.
331 *
332 * @param fid Pointer to the fibril structure of the fibril to be
333 * added.
334 *
335 */
336void fibril_add_manager(fid_t fid)
337{
338 fibril_t *fibril = (fibril_t *) fid;
339
340 futex_lock(&fibril_futex);
341 list_append(&fibril->link, &manager_list);
342 futex_unlock(&fibril_futex);
343}
344
345/** Remove one manager from the manager list. */
346void fibril_remove_manager(void)
347{
348 futex_lock(&fibril_futex);
349 if (!list_empty(&manager_list))
350 list_remove(list_first(&manager_list));
351 futex_unlock(&fibril_futex);
352}
353
354/** Return fibril id of the currently running fibril.
355 *
356 * @return fibril ID of the currently running fibril.
357 *
358 */
359fid_t fibril_get_id(void)
360{
361 return (fid_t) __tcb_get()->fibril_data;
362}
363
364/** @}
365 */
Note: See TracBrowser for help on using the repository browser.