Will something like below be helpful?
include/linux/printk.h | 8 ++++++++
include/linux/sched.h | 5 +++++
kernel/hung_task.c | 16 +++++++++++++---
kernel/printk/printk.c | 14 ++++++++++++++
lib/Kconfig.debug | 18 ++++++++++++++++++
net/core/dev.c | 2 ++
6 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ba36939fda79..2a2172e891a9 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -35,6 +35,24 @@ config PRINTK_CALLER
no option to enable/disable at the kernel command line parameter or
sysfs interface.
+config PRINTK_MESSAGE_BLOCK_MARKER
+ bool "Enable marker for grouping multiple lines of printk messages"
+ depends on PRINTK
+ help
+ Selecting this option helps grouping a series of printk() messages,
+ by manually inserting printk_block_{begin,end}() pairs into locations
+ where it makes sense (e.g. khungtaskd's stack traces followed by
+ lockdep's list of held locks).
+
+ A fuzzing system is currently using timeout based approach in order to
+ wait for completion of a series of kernel messages. But it will fail
+ if the cause of a series of kernel messages was a particularly high
+ load (e.g. most of CPU time being consumed by softirq context).
+ Nobody can know what timeout value will be enough. Therefore, by
+ emitting marker lines, the fuzzing system can figure out when a series
+ of kernel messages has been flushed; improving the likeliness of being
+ able to parse a series of kernel messages.
+
config STACKTRACE_BUILD_ID
bool "Show build ID information in stacktraces"
depends on PRINTK
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 45c663124c9b..60d47ec68459 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -330,6 +330,14 @@ static inline bool pr_flush(int timeout_ms, bool reset_on_progress)
#endif
+#ifdef CONFIG_PRINTK_MESSAGE_BLOCK_MARKER
+void printk_block_begin(void);
+void printk_block_end(void);
+#else
+static inline void printk_block_begin(void) { }
+static inline void printk_block_end(void) { }
+#endif
+
#ifdef CONFIG_SMP
extern int __printk_cpu_sync_try_get(void);
extern void __printk_cpu_sync_wait(void);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index d395f2810fac..974575bd6f12 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1638,6 +1638,11 @@ struct task_struct {
/* CPU-specific state of this task: */
struct thread_struct thread;
+
+#ifdef CONFIG_PRINTK_MESSAGE_BLOCK_MARKER
+ int printk_message_block_level;
+#endif
+
/*
* New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct.
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 1d765ad242b8..d33b59045830 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -4548,6 +4548,20 @@ bool pr_flush(int timeout_ms, bool reset_on_progress)
return __pr_flush(NULL, timeout_ms, reset_on_progress);
}
+#ifdef CONFIG_PRINTK_MESSAGE_BLOCK_MARKER
+void printk_block_begin(void)
+{
+ printk(KERN_INFO "<<< KERNEL MESSAGE BLOCK BEGIN >>> (%d)\n",
+ current->printk_message_block_level++);
+}
+
+void printk_block_end(void)
+{
+ printk(KERN_INFO "<<< KERNEL MESSAGE BLOCK END >>> (%d)\n",
+ --current->printk_message_block_level);
+}
+#endif
+
/*
* Delayed printk version, for scheduler-internal messages:
*/
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index d2254c91450b..570e9f387230 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -224,7 +224,7 @@ static inline void debug_show_blocker(struct task_struct *task, unsigned long ti
#endif
static void check_hung_task(struct task_struct *t, unsigned long timeout,
- unsigned long prev_detect_count)
+ unsigned long prev_detect_count, bool *started)
{
unsigned long total_hung_task;
@@ -252,6 +252,10 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout,
if (sysctl_hung_task_warnings || hung_task_call_panic) {
if (sysctl_hung_task_warnings > 0)
sysctl_hung_task_warnings--;
+ if (!*started) {
+ *started = true;
+ printk_block_begin();
+ }
pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n",
t->comm, t->pid, (jiffies - t->last_switch_time) / HZ);
pr_err(" %s %s %.*s\n",
@@ -308,6 +312,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
unsigned long prev_detect_count = sysctl_hung_task_detect_count;
int need_warning = sysctl_hung_task_warnings;
unsigned long si_mask = hung_task_si_mask;
+ bool started = false;
/*
* If the system crashed already then all bets are off,
@@ -328,13 +333,16 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
last_break = jiffies;
}
- check_hung_task(t, timeout, prev_detect_count);
+ check_hung_task(t, timeout, prev_detect_count, &started);
}
unlock:
rcu_read_unlock();
- if (!(sysctl_hung_task_detect_count - prev_detect_count))
+ if (!(sysctl_hung_task_detect_count - prev_detect_count)) {
+ if (started)
+ printk_block_end();
return;
+ }
if (need_warning || hung_task_call_panic) {
si_mask |= SYS_INFO_LOCKS;
@@ -344,6 +352,8 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
}
sys_info(si_mask);
+ if (started)
+ printk_block_end();
if (hung_task_call_panic)
panic("hung_task: blocked tasks");
diff --git a/net/core/dev.c b/net/core/dev.c
index 36dc5199037e..70a3c1c2108f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -11602,9 +11602,11 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list)
if (time_after(jiffies, warning_time +
READ_ONCE(netdev_unregister_timeout_secs) * HZ)) {
list_for_each_entry(dev, list, todo_list) {
+ printk_block_begin();
pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",
dev->name, netdev_refcnt_read(dev));
ref_tracker_dir_print(&dev->refcnt_tracker, 10);
+ printk_block_end();
}
warning_time = jiffies;
--
2.47.3