]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
sched_ext: Print sched_ext info when dumping stack
authorDavid Vernet <void@manifault.com>
Tue, 18 Jun 2024 20:09:18 +0000 (10:09 -1000)
committerTejun Heo <tj@kernel.org>
Tue, 18 Jun 2024 20:09:18 +0000 (10:09 -1000)
It would be useful to see what the sched_ext scheduler state is, and what
scheduler is running, when we're dumping a task's stack. This patch
therefore adds a new print_scx_info() function that's called in the same
context as print_worker_info() and print_stop_info(). An example dump
follows.

  BUG: kernel NULL pointer dereference, address: 0000000000000999
  #PF: supervisor write access in kernel mode
  #PF: error_code(0x0002) - not-present page
  PGD 0 P4D 0
  Oops: 0002 [#1] PREEMPT SMP
  CPU: 13 PID: 2047 Comm: insmod Tainted: G           O       6.6.0-work-10323-gb58d4cae8e99-dirty #34
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS unknown 2/2/2022
  Sched_ext: qmap (enabled+all), task: runnable_at=-17ms
  RIP: 0010:init_module+0x9/0x1000 [test_module]
  ...

v3: - scx_ops_enable_state_str[] definition moved to an earlier patch as
      it's now used by core implementation.

    - Convert jiffy delta to msecs using jiffies_to_msecs() instead of
      multiplying by (HZ / MSEC_PER_SEC). The conversion is implemented in
      jiffies_delta_msecs().

v2: - We are now using scx_ops_enable_state_str[] outside
      CONFIG_SCHED_DEBUG. Move it outside of CONFIG_SCHED_DEBUG and to the
      top. This was reported by Changwoo and Andrea.

Signed-off-by: David Vernet <void@manifault.com>
Reported-by: Changwoo Min <changwoo@igalia.com>
Reported-by: Andrea Righi <andrea.righi@canonical.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
include/linux/sched/ext.h
kernel/sched/core.c
kernel/sched/ext.c
lib/dump_stack.c

index ea7c501ac819243286bf23c73a31c1e5fa11e4a2..85fb5dc725ef68fc8b1c98031d120b82589c3f2c 100644 (file)
@@ -155,10 +155,12 @@ struct sched_ext_entity {
 };
 
 void sched_ext_free(struct task_struct *p);
+void print_scx_info(const char *log_lvl, struct task_struct *p);
 
 #else  /* !CONFIG_SCHED_CLASS_EXT */
 
 static inline void sched_ext_free(struct task_struct *p) {}
+static inline void print_scx_info(const char *log_lvl, struct task_struct *p) {}
 
 #endif /* CONFIG_SCHED_CLASS_EXT */
 #endif /* _LINUX_SCHED_EXT_H */
index f4365becdc131dc7b3faf93a6f7df103c913218d..1a3144c80af8a6bb8a8389347abdc58697489f3c 100644 (file)
@@ -7486,6 +7486,7 @@ void sched_show_task(struct task_struct *p)
 
        print_worker_info(KERN_INFO, p);
        print_stop_info(KERN_INFO, p);
+       print_scx_info(KERN_INFO, p);
        show_stack(p, NULL, KERN_INFO);
        put_task_stack(p);
 }
index 8ff30b80e8621b8c75a71bb1bf9b0c9fa97d668a..6f4de29d7372a1325f7b2a30e9f755b3558fff2b 100644 (file)
@@ -586,6 +586,14 @@ static __printf(3, 4) void scx_ops_exit_kind(enum scx_exit_kind kind,
 
 #define SCX_HAS_OP(op) static_branch_likely(&scx_has_op[SCX_OP_IDX(op)])
 
+static long jiffies_delta_msecs(unsigned long at, unsigned long now)
+{
+       if (time_after(at, now))
+               return jiffies_to_msecs(at - now);
+       else
+               return -(long)jiffies_to_msecs(now - at);
+}
+
 /* if the highest set bit is N, return a mask with bits [N+1, 31] set */
 static u32 higher_bits(u32 flags)
 {
@@ -3715,6 +3723,51 @@ static const struct sysrq_key_op sysrq_sched_ext_reset_op = {
        .enable_mask    = SYSRQ_ENABLE_RTNICE,
 };
 
+/**
+ * print_scx_info - print out sched_ext scheduler state
+ * @log_lvl: the log level to use when printing
+ * @p: target task
+ *
+ * If a sched_ext scheduler is enabled, print the name and state of the
+ * scheduler. If @p is on sched_ext, print further information about the task.
+ *
+ * This function can be safely called on any task as long as the task_struct
+ * itself is accessible. While safe, this function isn't synchronized and may
+ * print out mixups or garbages of limited length.
+ */
+void print_scx_info(const char *log_lvl, struct task_struct *p)
+{
+       enum scx_ops_enable_state state = scx_ops_enable_state();
+       const char *all = READ_ONCE(scx_switching_all) ? "+all" : "";
+       char runnable_at_buf[22] = "?";
+       struct sched_class *class;
+       unsigned long runnable_at;
+
+       if (state == SCX_OPS_DISABLED)
+               return;
+
+       /*
+        * Carefully check if the task was running on sched_ext, and then
+        * carefully copy the time it's been runnable, and its state.
+        */
+       if (copy_from_kernel_nofault(&class, &p->sched_class, sizeof(class)) ||
+           class != &ext_sched_class) {
+               printk("%sSched_ext: %s (%s%s)", log_lvl, scx_ops.name,
+                      scx_ops_enable_state_str[state], all);
+               return;
+       }
+
+       if (!copy_from_kernel_nofault(&runnable_at, &p->scx.runnable_at,
+                                     sizeof(runnable_at)))
+               scnprintf(runnable_at_buf, sizeof(runnable_at_buf), "%+ldms",
+                         jiffies_delta_msecs(runnable_at, jiffies));
+
+       /* print everything onto one line to conserve console space */
+       printk("%sSched_ext: %s (%s%s), task: runnable_at=%s",
+              log_lvl, scx_ops.name, scx_ops_enable_state_str[state], all,
+              runnable_at_buf);
+}
+
 void __init init_sched_ext_class(void)
 {
        s32 cpu, v;
index 222c6d6c8281a421b1a58f49b5e11e5048dce784..9581ef4efec54bf42f7389f59213ac0e9d4fe1e7 100644 (file)
@@ -68,6 +68,7 @@ void dump_stack_print_info(const char *log_lvl)
 
        print_worker_info(log_lvl, current);
        print_stop_info(log_lvl, current);
+       print_scx_info(log_lvl, current);
 }
 
 /**