Coverage Report

Created: 2026-09-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/task.c
Line
Count
Source
1
/*
2
 * Task management functions.
3
 *
4
 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10
 *
11
 */
12
13
#include <string.h>
14
15
#include <import/eb32tree.h>
16
17
#include <haproxy/api.h>
18
#include <haproxy/activity.h>
19
#include <haproxy/cfgparse.h>
20
#include <haproxy/clock.h>
21
#include <haproxy/fd.h>
22
#include <haproxy/list.h>
23
#include <haproxy/pool.h>
24
#include <haproxy/task.h>
25
#include <haproxy/tools.h>
26
27
extern struct task *process_stream(struct task *t, void *context, unsigned int state);
28
extern void stream_update_timings(struct task *t, uint64_t lat, uint64_t cpu);
29
30
DECLARE_TYPED_POOL(pool_head_task,    "task",    struct task, 0, 64);
31
DECLARE_TYPED_POOL(pool_head_tasklet, "tasklet", struct tasklet, 0, 64);
32
33
/* This is the memory pool containing all the signal structs. These
34
 * struct are used to store each required signal between two tasks.
35
 */
36
DECLARE_TYPED_POOL(pool_head_notification, "notification", struct notification);
37
38
/* used to detect if the scheduler looks stuck (for warnings) */
39
static struct {
40
  int sched_stuck THREAD_ALIGNED();
41
} sched_ctx[MAX_THREADS];
42
43
#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW)
44
__decl_thread(HA_SPINLOCK_T task_state_tid);
45
#endif
46
47
/* Flags the task <t> for immediate destruction and puts it into its first
48
 * thread's shared tasklet list if not yet queued/running. This will bypass
49
 * the priority scheduling and make the task show up as fast as possible in
50
 * the other thread's queue. Note that this operation isn't idempotent and is
51
 * not supposed to be run on the same task from multiple threads at once. It's
52
 * the caller's responsibility to make sure it is the only one able to kill the
53
 * task.
54
 */
55
void task_kill(struct task *t)
56
0
{
57
0
  unsigned int state = t->state;
58
0
  unsigned int thr;
59
60
0
  BUG_ON(state & TASK_KILLED);
61
62
0
  while (1) {
63
0
    while (state & (TASK_RUNNING | TASK_QUEUED)) {
64
      /* task already in the queue and about to be executed,
65
       * or even currently running. Just add the flag and be
66
       * done with it, the process loop will detect it and kill
67
       * it. The CAS will fail if we arrive too late.
68
       */
69
0
      if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_KILLED))
70
0
        return;
71
0
    }
72
73
    /* We'll have to wake it up, but we must also secure it so that
74
     * it doesn't vanish under us. TASK_QUEUED guarantees nobody will
75
     * add past us.
76
     */
77
0
    if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED | TASK_KILLED)) {
78
      /* Bypass the tree and go directly into the shared tasklet list.
79
       * Note: that's a task so it must be accounted for as such. Pick
80
       * the task's first thread for the job.
81
       */
82
0
      thr = __task_get_current_owner(t->tid);
83
0
      if (thr == -1)
84
0
        thr = tid;
85
86
      /* Beware: tasks that have never run don't have their ->list empty yet! */
87
0
      MT_LIST_APPEND(&ha_thread_ctx[thr].shared_tasklet_list,
88
0
                     list_to_mt_list(&((struct tasklet *)t)->list));
89
0
      _HA_ATOMIC_INC(&ha_thread_ctx[thr].rq_total);
90
0
      _HA_ATOMIC_INC(&ha_thread_ctx[thr].tasks_in_list);
91
0
      wake_thread(thr);
92
0
      return;
93
0
    }
94
0
  }
95
0
}
96
97
/* Equivalent of task_kill for tasklets. Mark the tasklet <t> for destruction.
98
 * It will be deleted on the next scheduler invocation. This function is
99
 * thread-safe : a thread can kill a tasklet of another thread.
100
 */
101
void tasklet_kill(struct tasklet *t)
102
0
{
103
0
  unsigned int state = t->state;
104
0
  unsigned int thr;
105
106
0
  BUG_ON(state & TASK_KILLED);
107
108
0
  while (1) {
109
0
    while (state & (TASK_QUEUED)) {
110
      /* Tasklet already in the list ready to be executed. Add
111
       * the killed flag and wait for the process loop to
112
       * detect it.
113
       */
114
0
      if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_KILLED))
115
0
        return;
116
0
    }
117
118
    /* Mark the tasklet as killed and wake the thread to process it
119
     * as soon as possible.
120
     */
121
0
    if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED | TASK_KILLED)) {
122
0
      thr = t->tid >= 0 ? t->tid : tid;
123
0
      MT_LIST_APPEND(&ha_thread_ctx[thr].shared_tasklet_list,
124
0
                     list_to_mt_list(&t->list));
125
0
      _HA_ATOMIC_INC(&ha_thread_ctx[thr].rq_total);
126
0
      wake_thread(thr);
127
0
      return;
128
0
    }
129
0
  }
130
0
}
131
132
/* Do not call this one, please use tasklet_wakeup_here() instead, as this one
133
 * is the slow path of tasklet_wakeup_here() which performs some preliminary
134
 * checks and sets TASK_QUEUED before calling this one. It is only permitted to
135
 * call this function for tasks/tasklets that are not bound (->tid==-1) or that
136
 * are bound to the local thread (->tid==tid || ->tid==-2-tid).
137
 */
138
void __tasklet_wakeup_here(struct tasklet *tl)
139
0
{
140
0
  BUG_ON_HOT(tl->tid != -1 && tl->tid != tid && tl->tid != -2 - tid);
141
142
0
  if (_HA_ATOMIC_LOAD(&th_ctx->flags) & TH_FL_TASK_PROFILING)
143
0
    tl->wake_date = now_mono_time();
144
145
  /* this tasklet runs on the caller thread */
146
0
  if (tl->state & TASK_HEAVY) {
147
0
    LIST_APPEND(&th_ctx->tasklets[TL_HEAVY], &tl->list);
148
0
    th_ctx->tl_class_mask |= 1 << TL_HEAVY;
149
0
  }
150
0
  else if (unlikely(tl->state & TASK_RT)) {
151
0
    LIST_APPEND(&th_ctx->tasklets[TL_RT], &tl->list);
152
0
    th_ctx->tl_class_mask |= 1 << TL_RT;
153
0
  }
154
0
  else if (tl->state & TASK_SELF_WAKING) {
155
0
    LIST_APPEND(&th_ctx->tasklets[TL_BULK], &tl->list);
156
0
    th_ctx->tl_class_mask |= 1 << TL_BULK;
157
0
  }
158
0
  else if ((struct task *)tl == th_ctx->current && !(tl->state & TASK_WOKEN_ANY)) {
159
0
    LIST_APPEND(&th_ctx->tasklets[TL_BULK], &tl->list);
160
0
    th_ctx->tl_class_mask |= 1 << TL_BULK;
161
0
  }
162
0
  else if (th_ctx->current_queue < 0) {
163
0
    LIST_APPEND(&th_ctx->tasklets[TL_URGENT], &tl->list);
164
0
    th_ctx->tl_class_mask |= 1 << TL_URGENT;
165
0
  }
166
0
  else {
167
0
    LIST_APPEND(&th_ctx->tasklets[TL_NORMAL], &tl->list);
168
0
    th_ctx->tl_class_mask |= 1 << TL_NORMAL;
169
0
  }
170
0
  _HA_ATOMIC_INC(&th_ctx->rq_total);
171
0
}
172
173
/* Do not call this one, please use tasklet_wakeup_on() instead, as this one is
174
 * the slow path of tasklet_wakeup_on() which performs some preliminary checks
175
 * and sets TASK_QUEUED before calling this one.
176
 */
177
void __tasklet_wakeup_on(struct tasklet *tl, int thr)
178
0
{
179
0
  BUG_ON_HOT(thr < 0);
180
181
0
  if (_HA_ATOMIC_LOAD(&ha_thread_ctx[thr].flags) & TH_FL_TASK_PROFILING)
182
0
    tl->wake_date = now_mono_time();
183
184
0
  MT_LIST_APPEND(&ha_thread_ctx[thr].shared_tasklet_list, list_to_mt_list(&tl->list));
185
0
  _HA_ATOMIC_INC(&ha_thread_ctx[thr].rq_total);
186
0
  wake_thread(thr);
187
0
}
188
189
/* Do not call this one, please use tasklet_wakeup_after_on() instead, as this one is
190
 * the slow path of tasklet_wakeup_after() which performs some preliminary checks
191
 * and sets TASK_QUEUED before calling this one.
192
 */
193
struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl)
194
0
{
195
0
  BUG_ON(tl->tid >= 0 && tid != tl->tid);
196
197
  /* this tasklet runs on the caller thread */
198
0
  if (!head) {
199
0
    __tasklet_wakeup_here(tl);
200
0
  }
201
0
  else {
202
0
    if (_HA_ATOMIC_LOAD(&th_ctx->flags) & TH_FL_TASK_PROFILING)
203
0
      tl->wake_date = now_mono_time();
204
0
    LIST_APPEND(head, &tl->list);
205
0
    _HA_ATOMIC_INC(&th_ctx->rq_total);
206
0
  }
207
0
  return &tl->list;
208
0
}
209
210
/* Puts the task <t> in run queue at a position depending on t->nice. <t> is
211
 * returned. The nice value assigns boosts in 32th of the run queue size. A
212
 * nice value of -1024 sets the task to -tasks_run_queue*32, while a nice value
213
 * of 1024 sets the task to tasks_run_queue*32. The state flags are cleared, so
214
 * the caller will have to set its flags after this call.
215
 * The task must not already be in the run queue. If unsure, use the safer
216
 * task_wakeup() function.
217
 */
218
void __task_wakeup(struct task *t)
219
0
{
220
0
  struct eb_root *root = &th_ctx->rqueue;
221
  /*
222
   * At this point the task tid should always be set to the relevant
223
   * thread, so we can just use __task_get_current_owner();
224
   */
225
0
  int thr __maybe_unused = __task_get_current_owner(t->tid);
226
227
0
  BUG_ON(t->tid == -1);
228
229
0
  if (unlikely(_HA_ATOMIC_LOAD(&t->state) & TASK_RT)) {
230
    /* real-time tasks must be super rare; they are woken up as tasklets. */
231
0
    if (thr < 0 || thr == tid)
232
0
      __tasklet_wakeup_here((struct tasklet *)t);
233
0
    else
234
0
      __tasklet_wakeup_on((struct tasklet *)t, thr);
235
0
    return;
236
0
  }
237
238
#ifdef USE_THREAD
239
  if (thr != tid) {
240
    root = &ha_thread_ctx[thr].rqueue_shared;
241
242
    _HA_ATOMIC_INC(&ha_thread_ctx[thr].rq_total);
243
    HA_SPIN_LOCK(TASK_RQ_LOCK, &ha_thread_ctx[thr].rqsh_lock);
244
245
    t->rq.key = _HA_ATOMIC_ADD_FETCH(&ha_thread_ctx[thr].rqueue_ticks, 1);
246
    __ha_barrier_store();
247
  } else
248
#endif
249
0
  {
250
0
    _HA_ATOMIC_INC(&th_ctx->rq_total);
251
0
    t->rq.key = _HA_ATOMIC_ADD_FETCH(&th_ctx->rqueue_ticks, 1);
252
0
  }
253
254
0
  if (likely(t->nice)) {
255
0
    int offset;
256
257
0
    _HA_ATOMIC_INC(&ha_thread_info[thr].tg_ctx->niced_tasks);
258
0
    offset = t->nice * (int)global.tune.runqueue_depth;
259
0
    t->rq.key += offset;
260
0
  }
261
262
0
  if (_HA_ATOMIC_LOAD(&th_ctx->flags) & TH_FL_TASK_PROFILING)
263
0
    t->wake_date = now_mono_time();
264
265
0
  eb32_insert(root, &t->rq);
266
267
#ifdef USE_THREAD
268
  if (thr != tid) {
269
    HA_SPIN_UNLOCK(TASK_RQ_LOCK, &ha_thread_ctx[thr].rqsh_lock);
270
271
    /* If all threads that are supposed to handle this task are sleeping,
272
     * wake one.
273
     */
274
    wake_thread(thr);
275
  }
276
#endif
277
0
  return;
278
0
}
279
280
/*
281
 * __task_queue()
282
 *
283
 * Inserts a task into wait queue <wq> at the position given by its expiration
284
 * date. It does not matter if the task was already in the wait queue or not,
285
 * as it will be unlinked. The task MUST NOT have an infinite expiration timer.
286
 * Last, tasks must not be queued further than the end of the tree, which is
287
 * between <now_ms> and <now_ms> + 2^31 ms (now+24days in 32bit).
288
 *
289
 * This function should not be used directly, it is meant to be called by the
290
 * inline version of task_queue() which performs a few cheap preliminary tests
291
 * before deciding to call __task_queue(). Moreover this function doesn't care
292
 * at all about locking so the caller must be careful when deciding whether to
293
 * lock or not around this call.
294
 */
295
void __task_queue(struct task *task)
296
0
{
297
0
  int old_state, new_state;
298
0
  int old_tid;
299
0
  int cur_owner;
300
301
  /* if this happens the process is doomed anyway, so better catch it now
302
   * so that we have the caller in the stack.
303
   */
304
0
  BUG_ON(task->expire == TICK_ETERNITY);
305
306
0
  do {
307
0
    new_state = old_state = _HA_ATOMIC_LOAD(&task->state);
308
0
    if (old_state & TASK_KILLED)
309
0
      return;
310
0
    old_tid = _HA_ATOMIC_LOAD(&task->tid);
311
0
    cur_owner = __task_get_current_owner(old_tid);
312
0
    if (old_tid != -1 && cur_owner != tid)
313
0
      new_state |= TASK_WOKEN_WQ;
314
0
  } while (!(__task_set_state_and_tid(task, old_tid, __task_get_new_tid_field(old_tid), old_state, new_state)));
315
316
0
  if (cur_owner != tid && cur_owner != -1) {
317
    /*
318
     * If the task has already been woken up to be added in the
319
     * wait queue, nothing left to do, the target thread will
320
     * eventually do the right thing.
321
     */
322
0
    if (!(old_state & TASK_WOKEN_WQ))
323
0
      _task_wakeup(task, 0, NULL);
324
0
    return;
325
0
  }
326
327
0
  if (likely(task_in_wq(task)))
328
0
    __task_unlink_wq(task);
329
330
  /* the task is not in the queue now */
331
0
  task->wq.key = task->expire;
332
#ifdef DEBUG_CHECK_INVALID_EXPIRATION_DATES
333
  if (tick_is_lt(task->wq.key, now_ms))
334
    /* we're queuing too far away or in the past (most likely) */
335
    return;
336
#endif
337
338
0
  eb32_insert(&th_ctx->timers, &task->wq);
339
0
}
340
341
/*
342
 * Extract all expired timers from the timer queue, and wakes up all
343
 * associated tasks.
344
 */
345
void wake_expired_tasks()
346
0
{
347
0
  struct thread_ctx * const tt = th_ctx; // thread's tasks
348
0
  int max_processed = global.tune.runqueue_depth;
349
0
  struct task *task;
350
0
  struct eb32_node *eb;
351
352
0
  while (1) {
353
0
    if (max_processed-- <= 0)
354
0
      goto leave;
355
356
0
    eb = eb32_lookup_ge(&tt->timers, now_ms - TIMER_LOOK_BACK);
357
0
    if (!eb) {
358
      /* we might have reached the end of the tree, typically because
359
      * <now_ms> is in the first half and we're first scanning the last
360
      * half. Let's loop back to the beginning of the tree now.
361
      */
362
0
      eb = eb32_first(&tt->timers);
363
0
      if (likely(!eb))
364
0
        break;
365
0
    }
366
367
    /* It is possible that this task was left at an earlier place in the
368
     * tree because a recent call to task_queue() has not moved it. This
369
     * happens when the new expiration date is later than the old one.
370
     * Since it is very unlikely that we reach a timeout anyway, it's a
371
     * lot cheaper to proceed like this because we almost never update
372
     * the tree. We may also find disabled expiration dates there. Since
373
     * we have detached the task from the tree, we simply call task_queue
374
     * to take care of this. Note that we might occasionally requeue it at
375
     * the same place, before <eb>, so we have to check if this happens,
376
     * and adjust <eb>, otherwise we may skip it which is not what we want.
377
     * We may also not requeue the task (and not point eb at it) if its
378
     * expiration time is not set. We also make sure we leave the real
379
     * expiration date for the next task in the queue so that when calling
380
     * next_timer_expiry() we're guaranteed to see the next real date and
381
     * not the next apparent date. This is in order to avoid useless
382
     * wakeups.
383
     */
384
385
0
    task = eb32_entry(eb, struct task, wq);
386
0
    if (tick_is_expired(task->expire, now_ms)) {
387
0
      int set_running = 0;
388
389
      /* expired task, wake it up */
390
0
      __task_unlink_wq(task);
391
      /*
392
       * If it's a shared task, see whether we should hand it
393
       * to a less loaded thread.
394
       */
395
0
      if (unlikely(task->tid < 0) && global.nbthread > 1) {
396
0
        int attempts = MIN(global.nbthread, 3);
397
0
        while (attempts-- > 0) {
398
0
          uint new_tid = statistical_prng_range(global.nbthread);
399
400
0
          if (new_tid == tid)
401
0
            continue;
402
403
0
          ASSUME(new_tid < MAX_THREADS);
404
0
          if (ha_thread_ctx[new_tid].rq_total * 2 < th_ctx->rq_total) {
405
0
            int cur_state;
406
0
            do {
407
0
              cur_state = _HA_ATOMIC_LOAD(&task->state);
408
              /*
409
               * Okay the task is already in our runqueue,
410
               * or somebody owns the
411
               * TASK_RUNNING flag because
412
               * it is calling task_schedule(), give up.
413
               */
414
0
              if (cur_state & (TASK_QUEUED | TASK_RUNNING))
415
0
                break;
416
              /*
417
               * Make sure we have TASK_RUNNING set
418
               * so that the task don't
419
               * immediately run on the
420
               * new thread and gets
421
               * freed.
422
               */
423
0
              if (__task_set_state_and_tid(task, task->tid, -2 - new_tid, cur_state, cur_state | TASK_RUNNING)) {
424
0
                set_running = 1;
425
0
                break;
426
0
              }
427
0
            } while (1);
428
0
            break;
429
0
          }
430
0
        }
431
0
      }
432
0
      if (set_running)
433
0
        task_drop_running(task, TASK_WOKEN_TIMER);
434
0
      else
435
0
        _task_wakeup(task, TASK_WOKEN_TIMER, 0);
436
0
    }
437
0
    else if (task->expire != eb->key) {
438
      /* task is not expired but its key doesn't match so let's
439
       * update it and skip to next apparently expired task.
440
       */
441
0
      __task_unlink_wq(task);
442
0
      if (tick_isset(task->expire))
443
0
        __task_queue(task);
444
0
    }
445
0
    else {
446
      /* task not expired and correctly placed. It may not be eternal. */
447
0
      BUG_ON(task->expire == TICK_ETERNITY);
448
0
      break;
449
0
    }
450
0
  }
451
0
leave:
452
0
  return;
453
0
}
454
455
/* Checks the next timer for the current thread by looking into its own timer
456
 * list. It may return TICK_ETERNITY if no timer is present.
457
 * Note that the next timer might very well be slightly in the past.
458
 */
459
int next_timer_expiry()
460
0
{
461
0
  struct thread_ctx * const tt = th_ctx; // thread's tasks
462
0
  struct eb32_node *eb;
463
0
  int ret = TICK_ETERNITY;
464
465
  /* first check in the thread-local timers */
466
0
  eb = eb32_lookup_ge(&tt->timers, now_ms - TIMER_LOOK_BACK);
467
0
  if (!eb) {
468
    /* we might have reached the end of the tree, typically because
469
     * <now_ms> is in the first half and we're first scanning the last
470
     * half. Let's loop back to the beginning of the tree now.
471
     */
472
0
    eb = eb32_first(&tt->timers);
473
0
  }
474
475
0
  if (eb)
476
0
    ret = eb->key;
477
478
0
  return ret;
479
0
}
480
481
/* Walks over tasklet lists th_ctx->tasklets[0..TL_CLASSES-1] and run at most
482
 * budget[TL_*] of them. Returns the number of entries effectively processed
483
 * (tasks and tasklets merged). The count of tasks in the list for the current
484
 * thread is adjusted.
485
 */
486
unsigned int run_tasks_from_lists(unsigned int budgets[])
487
0
{
488
0
  struct task *(*process)(struct task *t, void *ctx, unsigned int state);
489
0
  struct list *tl_queues = th_ctx->tasklets;
490
0
  struct task *t;
491
0
  uint8_t budget_mask = (1 << TL_CLASSES) - 1;
492
0
  struct sched_activity *profile_entry = NULL;
493
0
  unsigned int done = 0;
494
0
  unsigned int queue;
495
0
  unsigned int state;
496
0
  void *ctx;
497
498
0
  for (queue = 0; queue < TL_CLASSES;) {
499
0
    th_ctx->current_queue = queue;
500
501
    /* global.tune.sched.low-latency is set */
502
0
    if (global.tune.options & GTUNE_SCHED_LOW_LATENCY) {
503
0
      if (unlikely(th_ctx->tl_class_mask & budget_mask & ((1 << queue) - 1))) {
504
        /* a lower queue index has tasks again and still has a
505
         * budget to run them. Let's switch to it now.
506
         */
507
0
        queue = (th_ctx->tl_class_mask & 1) ? 0 :
508
0
          (th_ctx->tl_class_mask & 2) ? 1 :
509
0
          (th_ctx->tl_class_mask & 4) ? 2 : 3;
510
0
        continue;
511
0
      }
512
513
0
      if (unlikely(queue > TL_URGENT &&
514
0
             budget_mask & (1 << TL_URGENT) &&
515
0
             !MT_LIST_ISEMPTY(&th_ctx->shared_tasklet_list))) {
516
        /* an urgent tasklet arrived from another thread */
517
0
        break;
518
0
      }
519
520
0
      if (unlikely(queue > TL_NORMAL &&
521
0
             budget_mask & (1 << TL_NORMAL) &&
522
0
             (!eb_is_empty(&th_ctx->rqueue) || !eb_is_empty(&th_ctx->rqueue_shared)))) {
523
        /* a task was woken up by a bulk tasklet or another thread */
524
0
        break;
525
0
      }
526
0
    }
527
528
0
    if (LIST_ISEMPTY(&tl_queues[queue])) {
529
0
      th_ctx->tl_class_mask &= ~(1 << queue);
530
0
      queue++;
531
0
      continue;
532
0
    }
533
534
0
    if (!budgets[queue]) {
535
0
      budget_mask &= ~(1 << queue);
536
0
      queue++;
537
0
      continue;
538
0
    }
539
540
0
    t = (struct task *)LIST_ELEM(tl_queues[queue].n, struct tasklet *, list);
541
542
    /* check if this task has already run during this loop */
543
0
    if ((uint16_t)t->last_run == (uint16_t)activity[tid].loops) {
544
0
      budget_mask &= ~(1 << queue);
545
0
      queue++;
546
0
      continue;
547
0
    }
548
0
    t->last_run = activity[tid].loops;
549
0
    ctx = t->context;
550
0
    process = t->process;
551
0
    t->calls++;
552
553
0
    budgets[queue]--;
554
0
    activity[tid].ctxsw++;
555
556
0
    th_ctx->lock_wait_total = 0;
557
0
    th_ctx->mem_wait_total = 0;
558
0
    th_ctx->locked_total = 0;
559
0
    th_ctx->sched_wake_date = t->wake_date;
560
0
    if (th_ctx->sched_wake_date || (t->state & TASK_F_WANTS_TIME)) {
561
      /* take the most accurate clock we have, either
562
       * mono_time() or last now_ns (monotonic but only
563
       * incremented once per poll loop).
564
       */
565
0
      th_ctx->sched_call_date = now_mono_time();
566
0
      if (unlikely(!th_ctx->sched_call_date))
567
0
        th_ctx->sched_call_date = now_ns;
568
0
    }
569
570
0
    if (th_ctx->sched_wake_date) {
571
0
      t->wake_date = 0;
572
0
      profile_entry = sched_activity_entry(sched_activity, t->process, t->caller);
573
0
      th_ctx->sched_profile_entry = profile_entry;
574
0
      HA_ATOMIC_ADD(&profile_entry->lat_time, (uint32_t)(th_ctx->sched_call_date - th_ctx->sched_wake_date));
575
0
      HA_ATOMIC_INC(&profile_entry->calls);
576
0
    }
577
578
0
    __ha_barrier_store();
579
580
0
    th_ctx->current = t;
581
0
    _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_STUCK); // this thread is still running
582
583
0
    _HA_ATOMIC_DEC(&th_ctx->rq_total);
584
0
    LIST_DEL_INIT(&((struct tasklet *)t)->list);
585
0
    __ha_barrier_store();
586
587
588
    /* We must be the exclusive owner of the TASK_RUNNING bit, and
589
     * have to be careful that the task is not being manipulated on
590
     * another thread finding it expired in wake_expired_tasks().
591
     * The TASK_RUNNING bit will be set during these operations,
592
     * they are extremely rare and do not last long so the best to
593
     * do here is to wait.
594
     */
595
0
    state = _HA_ATOMIC_LOAD(&t->state);
596
0
    do {
597
0
      while (unlikely(state & TASK_RUNNING)) {
598
0
        __ha_cpu_relax();
599
0
        state = _HA_ATOMIC_LOAD(&t->state);
600
0
      }
601
0
    } while (!_HA_ATOMIC_CAS(&t->state, &state, (state & TASK_PERSISTENT) | TASK_RUNNING));
602
603
0
    __ha_barrier_atomic_store();
604
605
    /* keep the task counter up to date */
606
0
    if (!(state & TASK_F_TASKLET))
607
0
      _HA_ATOMIC_DEC(&ha_thread_ctx[tid].tasks_in_list);
608
609
    /* From this point, we know that the task or tasklet was properly
610
     * dequeued, flagged and accounted for. Let's now check if it was
611
     * killed. If TASK_KILLED arrived before we've read the state, we
612
     * directly free the task/tasklet. Otherwise for tasks it will be
613
     * seen after processing and it's freed on the exit path.
614
     */
615
616
0
    if (unlikely((state & TASK_KILLED) || process == NULL)) {
617
      /* Task or tasklet has been killed, let's remove it */
618
0
      if (state & TASK_F_TASKLET)
619
0
        pool_free(pool_head_tasklet, t);
620
0
      else {
621
0
        task_unlink_wq(t);
622
0
        __task_free(t);
623
0
      }
624
      /* We don't want max_processed to be decremented if
625
       * we're just freeing a destroyed task, we should only
626
       * do so if we really ran a task.
627
       */
628
0
      goto next;
629
0
    }
630
631
0
    if (state & TASK_WOKEN_WQ) {
632
      /* We should add this task to our wait queue */
633
0
      task_queue(t);
634
      /*
635
       * If this is the only reason the task got scheduled,
636
       * then we don't actually have ot run it.
637
       */
638
0
      if ((state & TASK_WOKEN_ANY) == TASK_WOKEN_WQ) {
639
0
        task_drop_running(t, 0);
640
0
        goto next;
641
0
      }
642
0
      state &= ~TASK_WOKEN_WQ;
643
0
    }
644
    /* OK now the task or tasklet is well alive and is going to be run */
645
0
    if (state & TASK_F_TASKLET) {
646
      /* this is a tasklet */
647
648
0
      t = EXEC_CTX_WITH_RET(EXEC_CTX_MAKE(TH_EX_CTX_TASK, process),
649
0
                            process(t, ctx, state));
650
0
      if (t != NULL)
651
0
        _HA_ATOMIC_AND(&t->state, ~TASK_RUNNING);
652
0
    } else {
653
      /* This is a regular task */
654
655
0
      if (process == process_stream)
656
0
        t = EXEC_CTX_WITH_RET(EXEC_CTX_MAKE(TH_EX_CTX_TASK, process_stream),
657
0
                              process_stream(t, ctx, state));
658
0
      else
659
0
        t = EXEC_CTX_WITH_RET(EXEC_CTX_MAKE(TH_EX_CTX_TASK, process),
660
0
                                    process(t, ctx, state));
661
662
      /* If there is a pending state, we have to wake up the task
663
       * immediately, else we defer it into wait queue.
664
       */
665
0
      if (t != NULL) {
666
0
        state = _HA_ATOMIC_LOAD(&t->state);
667
0
        if (unlikely(state & TASK_KILLED)) {
668
0
          task_unlink_wq(t);
669
0
          __task_free(t);
670
0
        }
671
0
        else {
672
0
          if (__task_get_current_owner(t->tid) == tid)
673
0
            task_queue(t);
674
0
          task_drop_running(t, 0);
675
0
        }
676
0
      }
677
0
    }
678
0
    done++;
679
0
  next:
680
0
    th_ctx->current = NULL;
681
0
    sched_ctx[tid].sched_stuck = 0; // scheduler is not stuck (don't warn)
682
0
    __ha_barrier_store();
683
684
    /* stats are only registered for non-zero wake dates */
685
0
    if (unlikely(th_ctx->sched_wake_date)) {
686
0
      HA_ATOMIC_ADD(&profile_entry->cpu_time, (uint32_t)(now_mono_time() - th_ctx->sched_call_date));
687
0
      if (th_ctx->lock_wait_total)
688
0
        HA_ATOMIC_ADD(&profile_entry->lkw_time, th_ctx->lock_wait_total);
689
0
      if (th_ctx->mem_wait_total)
690
0
        HA_ATOMIC_ADD(&profile_entry->mem_time, th_ctx->mem_wait_total);
691
0
      if (th_ctx->locked_total)
692
0
        HA_ATOMIC_ADD(&profile_entry->lkd_time, th_ctx->locked_total);
693
0
    }
694
0
  }
695
0
  th_ctx->current_queue = -1;
696
0
  th_ctx->sched_wake_date = TICK_ETERNITY;
697
698
0
  return done;
699
0
}
700
701
/* The run queue is chronologically sorted in a tree. An insertion counter is
702
 * used to assign a position to each task. This counter may be combined with
703
 * other variables (eg: nice value) to set the final position in the tree. The
704
 * counter may wrap without a problem, of course. We then limit the number of
705
 * tasks processed to 200 in any case, so that general latency remains low and
706
 * so that task positions have a chance to be considered. The function scans
707
 * both the global and local run queues and picks the most urgent task between
708
 * the two. We need to grab the global runqueue lock to touch it so it's taken
709
 * on the very first access to the global run queue and is released as soon as
710
 * it reaches the end.
711
 *
712
 * The function adjusts <next> if a new event is closer.
713
 */
714
void process_runnable_tasks()
715
0
{
716
0
  struct thread_ctx * const tt = th_ctx;
717
0
  struct eb32_node *lrq; // next local run queue entry
718
0
  struct eb32_node *grq; // next global run queue entry
719
0
  struct task *t;
720
0
  const unsigned int default_weights[TL_CLASSES] = {
721
0
    [TL_RT]     = 1,  // never more than 1 RT task at once
722
0
    [TL_URGENT] = 64, // ~50% of CPU bandwidth for I/O
723
0
    [TL_NORMAL] = 60, // ~47% of CPU bandwidth for tasks
724
0
    [TL_BULK]   = 4,  // ~3% of CPU bandwidth for self-wakers
725
0
    [TL_HEAVY]  = 1,  // never more than 1 heavy task at once
726
0
  };
727
0
  unsigned int max[TL_CLASSES]; // max to be run per class
728
0
  unsigned int max_total;       // sum of max above
729
0
  struct mt_list *tmp_list;
730
0
  unsigned int queue;
731
0
  int max_processed;
732
0
  int lpicked, gpicked;
733
0
  int rt_queued = 0;
734
0
  int heavy_queued = 0;
735
0
  int budget, done;
736
737
0
  _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_STUCK); // this thread is still running
738
739
0
  swrate_add_peak_local(&th_ctx->rq_tot_peak, RQ_LOAD_SAMPLES, th_ctx->rq_total);
740
741
0
  if (!thread_has_tasks()) {
742
0
    activity[tid].empty_rq++;
743
0
    return;
744
0
  }
745
746
0
  max_processed = global.tune.runqueue_depth;
747
748
0
  if (likely(tg_ctx->niced_tasks))
749
0
    max_processed = (max_processed + 3) / 4;
750
751
0
  if (max_processed < th_ctx->rq_total && th_ctx->rq_total <= 2*max_processed) {
752
    /* If the run queue exceeds the budget by up to 50%, let's cut it
753
     * into two identical halves to improve latency.
754
     */
755
0
    max_processed = th_ctx->rq_total / 2;
756
0
  }
757
758
0
 not_done_yet:
759
0
  max[TL_URGENT] = max[TL_NORMAL] = max[TL_BULK] = 0;
760
761
  /* RT tasklets list may be processed at most once */
762
0
  if (!rt_queued) {
763
0
    if ((tt->tl_class_mask & (1 << TL_RT))) {
764
0
      max[TL_RT] = default_weights[TL_RT];
765
0
      rt_queued = 1;
766
0
    }
767
0
    else
768
0
      max[TL_RT] = 0;
769
0
  }
770
771
  /* urgent tasklets list gets a default weight of ~50% */
772
0
  if ((tt->tl_class_mask & (1 << TL_URGENT)) ||
773
0
      !MT_LIST_ISEMPTY(&tt->shared_tasklet_list))
774
0
    max[TL_URGENT] = default_weights[TL_URGENT];
775
776
  /* normal tasklets list gets a default weight of ~47% */
777
0
  if ((tt->tl_class_mask & (1 << TL_NORMAL)) ||
778
0
      !eb_is_empty(&th_ctx->rqueue) || !eb_is_empty(&th_ctx->rqueue_shared))
779
0
    max[TL_NORMAL] = default_weights[TL_NORMAL];
780
781
  /* bulk tasklets list gets a default weight of ~3% */
782
0
  if ((tt->tl_class_mask & (1 << TL_BULK)))
783
0
    max[TL_BULK] = default_weights[TL_BULK];
784
785
  /* heavy tasks are processed only once and never refilled in a
786
   * call round. That budget is not lost either as we don't reset
787
   * it unless consumed.
788
   */
789
0
  if (!heavy_queued) {
790
0
    if ((tt->tl_class_mask & (1 << TL_HEAVY)))
791
0
      max[TL_HEAVY] = default_weights[TL_HEAVY];
792
0
    else
793
0
      max[TL_HEAVY] = 0;
794
0
    heavy_queued = 1;
795
0
  }
796
797
  /* Now compute a fair share of the weights. Total may slightly exceed
798
   * 100% due to rounding, this is not a problem. Note that while in
799
   * theory the sum cannot be NULL as we cannot get there without tasklets
800
   * to process, in practice it seldom happens when multiple writers
801
   * conflict and rollback on MT_LIST_TRY_APPEND(shared_tasklet_list), causing
802
   * a first MT_LIST_ISEMPTY() to succeed for thread_has_task() and the
803
   * one above to finally fail. This is extremely rare and not a problem.
804
   */
805
0
  max_total = max[TL_RT] + max[TL_URGENT] + max[TL_NORMAL] + max[TL_BULK] + max[TL_HEAVY];
806
0
  if (!max_total)
807
0
    goto leave;
808
809
0
  for (queue = 0; queue < TL_CLASSES; queue++)
810
0
    max[queue]  = ((unsigned)max_processed * max[queue] + max_total - 1) / max_total;
811
812
  /* The RT queue must never process more than one task at once */
813
0
  if (max[TL_RT] > 1)
814
0
    max[TL_RT] = 1;
815
816
  /* The heavy queue must never process more than very few tasks at once
817
   * anyway. We set the limit to 1 if running on low_latency scheduling,
818
   * given that we know that other values can have an impact on latency
819
   * (~500us end-to-end connection achieved at 130kcps in SSL), 1 + one
820
   * per 1024 tasks if there is at least one non-heavy task while still
821
   * respecting the ratios above, or 1 + one per 128 tasks if only heavy
822
   * tasks are present. This allows to drain excess SSL handshakes more
823
   * efficiently if the queue becomes congested.
824
   */
825
0
  if (max[TL_HEAVY] > 1) {
826
0
    if (global.tune.options & GTUNE_SCHED_LOW_LATENCY)
827
0
      budget = 1;
828
0
    else if (tt->tl_class_mask & ~(1 << TL_HEAVY))
829
0
      budget = 1 + tt->rq_total / 1024;
830
0
    else
831
0
      budget = 1 + tt->rq_total / 128;
832
833
0
    if (max[TL_HEAVY] > budget)
834
0
      max[TL_HEAVY] = budget;
835
0
  }
836
837
0
  lrq = grq = NULL;
838
839
  /* pick up to max[TL_NORMAL] regular tasks from prio-ordered run queues */
840
  /* Note: the grq lock is always held when grq is not null */
841
0
  lpicked = gpicked = 0;
842
0
  budget = max[TL_NORMAL] - tt->tasks_in_list;
843
0
  while (lpicked + gpicked < budget && (!rt_queued || !(global.tune.options & GTUNE_SCHED_LOW_LATENCY))) {
844
0
    if (!eb_is_empty(&th_ctx->rqueue_shared) && !grq) {
845
#ifdef USE_THREAD
846
      HA_SPIN_LOCK(TASK_RQ_LOCK, &th_ctx->rqsh_lock);
847
      grq = eb32_lookup_ge(&th_ctx->rqueue_shared, _HA_ATOMIC_LOAD(&tt->rqueue_ticks) - TIMER_LOOK_BACK);
848
      if (unlikely(!grq)) {
849
        grq = eb32_first(&th_ctx->rqueue_shared);
850
        if (!grq)
851
          HA_SPIN_UNLOCK(TASK_RQ_LOCK, &th_ctx->rqsh_lock);
852
      }
853
#endif
854
0
    }
855
856
    /* If a global task is available for this thread, it's in grq
857
     * now and the global RQ is locked.
858
     */
859
860
0
    if (!lrq) {
861
0
      lrq = eb32_lookup_ge(&tt->rqueue, _HA_ATOMIC_LOAD(&tt->rqueue_ticks) - TIMER_LOOK_BACK);
862
0
      if (unlikely(!lrq))
863
0
        lrq = eb32_first(&tt->rqueue);
864
0
    }
865
866
0
    if (!lrq && !grq)
867
0
      break;
868
869
0
    if (likely(!grq || (lrq && (int)(lrq->key - grq->key) <= 0))) {
870
0
      t = eb32_entry(lrq, struct task, rq);
871
0
      lrq = eb32_next(lrq);
872
0
      eb32_delete(&t->rq);
873
0
      lpicked++;
874
0
    }
875
#ifdef USE_THREAD
876
    else {
877
      t = eb32_entry(grq, struct task, rq);
878
      grq = eb32_next(grq);
879
      eb32_delete(&t->rq);
880
881
      if (unlikely(!grq)) {
882
        grq = eb32_first(&th_ctx->rqueue_shared);
883
        if (!grq)
884
          HA_SPIN_UNLOCK(TASK_RQ_LOCK, &th_ctx->rqsh_lock);
885
      }
886
      gpicked++;
887
    }
888
#endif
889
0
    if (t->nice)
890
0
      _HA_ATOMIC_DEC(&tg_ctx->niced_tasks);
891
892
    /* Add it to the local task list */
893
0
    LIST_APPEND(&tt->tasklets[TL_NORMAL], &((struct tasklet *)t)->list);
894
0
  }
895
896
  /* release the rqueue lock */
897
0
  if (grq) {
898
0
    HA_SPIN_UNLOCK(TASK_RQ_LOCK, &th_ctx->rqsh_lock);
899
0
    grq = NULL;
900
0
  }
901
902
0
  if (lpicked + gpicked) {
903
0
    tt->tl_class_mask |= 1 << TL_NORMAL;
904
0
    _HA_ATOMIC_ADD(&tt->tasks_in_list, lpicked + gpicked);
905
0
    activity[tid].tasksw += lpicked + gpicked;
906
0
  }
907
908
  /* Merge the list of tasklets waken up by other threads to the
909
   * main list.
910
   */
911
0
  tmp_list = MT_LIST_BEHEAD(&tt->shared_tasklet_list);
912
0
  if (tmp_list) {
913
0
    LIST_SPLICE_END_DETACHED(&tt->tasklets[TL_URGENT], (struct list *)tmp_list);
914
0
    if (!LIST_ISEMPTY(&tt->tasklets[TL_URGENT]))
915
0
      tt->tl_class_mask |= 1 << TL_URGENT;
916
0
  }
917
918
  /* execute tasklets in each queue */
919
0
  done = run_tasks_from_lists(max);
920
0
  max_processed -= done;
921
922
  /* some tasks may have woken other ones up */
923
0
  if (done && max_processed > 0 && !rt_queued && thread_has_tasks())
924
0
    goto not_done_yet;
925
926
0
 leave:
927
0
  if (tt->tl_class_mask)
928
0
    activity[tid].long_rq++;
929
0
}
930
931
/* Pings the scheduler to verify that tasks continue running for thread <thr>.
932
 * Returns 1 if the scheduler made progress since last call, 0 if it looks
933
 * stuck. It marks it as stuck for next visit.
934
 */
935
int is_sched_alive(int thr)
936
0
{
937
0
  return !HA_ATOMIC_XCHG(&sched_ctx[thr].sched_stuck, 1);
938
0
}
939
940
/*
941
 * Delete every tasks before running the master polling loop
942
 */
943
void mworker_cleantasks()
944
0
{
945
0
  struct task *t;
946
0
  int i;
947
0
  struct eb32_node *tmp_wq = NULL;
948
0
  struct eb32_node *tmp_rq = NULL;
949
950
#ifdef USE_THREAD
951
  /* cleanup the global run queue */
952
  tmp_rq = eb32_first(&th_ctx->rqueue_shared);
953
  while (tmp_rq) {
954
    t = eb32_entry(tmp_rq, struct task, rq);
955
    tmp_rq = eb32_next(tmp_rq);
956
    task_destroy(t);
957
  }
958
#endif
959
  /* clean the per thread run queue */
960
0
  for (i = 0; i < global.nbthread; i++) {
961
0
    tmp_rq = eb32_first(&ha_thread_ctx[i].rqueue);
962
0
    while (tmp_rq) {
963
0
      t = eb32_entry(tmp_rq, struct task, rq);
964
0
      tmp_rq = eb32_next(tmp_rq);
965
0
      task_destroy(t);
966
0
    }
967
    /* cleanup the per thread timers queue */
968
0
    tmp_wq = eb32_first(&ha_thread_ctx[i].timers);
969
0
    while (tmp_wq) {
970
0
      t = eb32_entry(tmp_wq, struct task, wq);
971
0
      tmp_wq = eb32_next(tmp_wq);
972
0
      task_destroy(t);
973
0
    }
974
0
  }
975
0
}
976
977
/* perform minimal initializations */
978
static void init_task()
979
0
{
980
0
  int i, q;
981
982
0
  for (i = 0; i < MAX_THREADS; i++) {
983
0
    for (q = 0; q < TL_CLASSES; q++)
984
0
      LIST_INIT(&ha_thread_ctx[i].tasklets[q]);
985
0
    MT_LIST_INIT(&ha_thread_ctx[i].shared_tasklet_list);
986
0
  }
987
0
}
988
989
/* config parser for global "tune.sched.low-latency", accepts "on" or "off" */
990
static int cfg_parse_tune_sched_low_latency(char **args, int section_type, struct proxy *curpx,
991
                                      const struct proxy *defpx, const char *file, int line,
992
                                      char **err)
993
0
{
994
0
  if (too_many_args(1, args, err, NULL))
995
0
    return -1;
996
997
0
  if (strcmp(args[1], "on") == 0)
998
0
    global.tune.options |= GTUNE_SCHED_LOW_LATENCY;
999
0
  else if (strcmp(args[1], "off") == 0)
1000
0
    global.tune.options &= ~GTUNE_SCHED_LOW_LATENCY;
1001
0
  else {
1002
0
    memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]);
1003
0
    return -1;
1004
0
  }
1005
0
  return 0;
1006
0
}
1007
1008
/* config keyword parsers */
1009
static struct cfg_kw_list cfg_kws = {ILH, {
1010
  { CFG_GLOBAL, "tune.sched.low-latency", cfg_parse_tune_sched_low_latency },
1011
  { 0, NULL, NULL }
1012
}};
1013
1014
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
1015
INITCALL0(STG_PREPARE, init_task);
1016
1017
/*
1018
 * Local variables:
1019
 *  c-indent-level: 8
1020
 *  c-basic-offset: 8
1021
 * End:
1022
 */