]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: task/debug: make task_queue() and task_schedule() possible callers
authorWilly Tarreau <w@1wt.eu>
Thu, 9 Nov 2023 11:05:08 +0000 (12:05 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 9 Nov 2023 16:24:00 +0000 (17:24 +0100)
It's common to see process_stream() being woken up by wake_expired_tasks
in the profiling output, without knowing which timeout was set to cause
this. By making it possible to record the call places of task_queue()
and task_schedule(), and by making wake_expired_tasks() explicitly not
replace it, we'll be able to know which task_queue() or task_schedule()
was triggered for a given wakeup.

For example below:
  process_stream                51200   311.4ms   6.081us   34.59s    675.6us <- run_tasks_from_lists@src/task.c:659 task_queue
  process_stream                19227   70.00ms   3.640us   9.813m    30.62ms <- sc_notify@src/stconn.c:1136 task_wakeup
  process_stream                 6414   102.3ms   15.95us   8.093m    75.70ms <- stream_new@src/stream.c:578 task_wakeup

It's visible that it's the run_tasks_from_lists() which in fact applies
on the task->expire returned by the ->process() function itself.

include/haproxy/task-t.h
include/haproxy/task.h
src/task.c

index 4dd10f438d6de7f2eb1b3f4a492c16412a27fc23..43f13e7b9cb442e464602f9740a0b52a00445fdb 100644 (file)
@@ -95,6 +95,8 @@ enum {
        WAKEUP_TYPE_TASKLET_WAKEUP,
        WAKEUP_TYPE_TASKLET_WAKEUP_AFTER,
        WAKEUP_TYPE_TASK_DROP_RUNNING,
+       WAKEUP_TYPE_TASK_SCHEDULE,
+       WAKEUP_TYPE_TASK_QUEUE,
        WAKEUP_TYPE_APPCTX_WAKEUP,
 };
 
index 592d9c1e5082a805373d40e6e0c9d01bf424c371..df9e4ff7c55dd19c2057271a19d9973c871562a4 100644 (file)
@@ -298,7 +298,10 @@ static inline struct task *task_unlink_wq(struct task *t)
  * protected by the global wq_lock, otherwise by it necessarily belongs to the
  * current thread'sand is queued without locking.
  */
-static inline void task_queue(struct task *task)
+#define task_queue(t) \
+       _task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0))
+
+static inline void _task_queue(struct task *task, const struct ha_caller *caller)
 {
        /* If we already have a place in the wait queue no later than the
         * timeout we're trying to set, we'll stay there, because it is very
@@ -315,15 +318,31 @@ static inline void task_queue(struct task *task)
 #ifdef USE_THREAD
        if (task->tid < 0) {
                HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
-               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key))
+               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
+                       if (likely(caller)) {
+                               caller = HA_ATOMIC_XCHG(&task->caller, caller);
+                               BUG_ON((ulong)caller & 1);
+#ifdef DEBUG_TASK
+                               HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
+#endif
+                       }
                        __task_queue(task, &tg_ctx->timers);
+               }
                HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
        } else
 #endif
        {
                BUG_ON(task->tid != tid);
-               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key))
+               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
+                       if (likely(caller)) {
+                               caller = HA_ATOMIC_XCHG(&task->caller, caller);
+                               BUG_ON((ulong)caller & 1);
+#ifdef DEBUG_TASK
+                               HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
+#endif
+                       }
                        __task_queue(task, &th_ctx->timers);
+               }
        }
 }
 
@@ -677,7 +696,10 @@ static inline void tasklet_set_tid(struct tasklet *tl, int tid)
  * now_ms without using tick_add() will definitely make this happen once every
  * 49.7 days.
  */
-static inline void task_schedule(struct task *task, int when)
+#define task_schedule(t, w) \
+       _task_schedule(t, w, MK_CALLER(WAKEUP_TYPE_TASK_SCHEDULE, 0, 0))
+
+static inline void _task_schedule(struct task *task, int when, const struct ha_caller *caller)
 {
        /* TODO: mthread, check if there is no tisk with this test */
        if (task_in_rq(task))
@@ -691,8 +713,16 @@ static inline void task_schedule(struct task *task, int when)
                        when = tick_first(when, task->expire);
 
                task->expire = when;
-               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key))
+               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
+                       if (likely(caller)) {
+                               caller = HA_ATOMIC_XCHG(&task->caller, caller);
+                               BUG_ON((ulong)caller & 1);
+#ifdef DEBUG_TASK
+                               HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
+#endif
+                       }
                        __task_queue(task, &tg_ctx->timers);
+               }
                HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
        } else
 #endif
@@ -702,8 +732,16 @@ static inline void task_schedule(struct task *task, int when)
                        when = tick_first(when, task->expire);
 
                task->expire = when;
-               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key))
+               if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
+                       if (likely(caller)) {
+                               caller = HA_ATOMIC_XCHG(&task->caller, caller);
+                               BUG_ON((ulong)caller & 1);
+#ifdef DEBUG_TASK
+                               HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
+#endif
+                       }
                        __task_queue(task, &th_ctx->timers);
+               }
        }
 }
 
@@ -718,6 +756,8 @@ static inline const char *task_wakeup_type_str(uint t)
        case WAKEUP_TYPE_TASKLET_WAKEUP       : return "tasklet_wakeup";
        case WAKEUP_TYPE_TASKLET_WAKEUP_AFTER : return "tasklet_wakeup_after";
        case WAKEUP_TYPE_TASK_DROP_RUNNING    : return "task_drop_running";
+       case WAKEUP_TYPE_TASK_QUEUE           : return "task_queue";
+       case WAKEUP_TYPE_TASK_SCHEDULE        : return "task_schedule";
        case WAKEUP_TYPE_APPCTX_WAKEUP        : return "appctx_wakeup";
        default                               : return "?";
        }
index 12b24b460c499968a454e5005c4e85158ac48b7f..3e351d0e33b55ee7d0b15bb60cdf2eb33cd92d02 100644 (file)
@@ -347,7 +347,7 @@ void wake_expired_tasks()
                if (tick_is_expired(task->expire, now_ms)) {
                        /* expired task, wake it up */
                        __task_unlink_wq(task);
-                       task_wakeup(task, TASK_WOKEN_TIMER);
+                       _task_wakeup(task, TASK_WOKEN_TIMER, 0);
                }
                else if (task->expire != eb->key) {
                        /* task is not expired but its key doesn't match so let's