[RFC][PATCHv6 10/12] printk: move offloading logic to per-cpu

Sergey Senozhatsky
2017-12-04T13:49:53Z

We have a global offloading state and make the offloading
decision based on printing task pointer and elapsed time.
If we keep seeing the same task performing printing for too
long we request offloading; otherwise, when we see that
printing is now performed by another task, we reset the
printing task pointer and its elapsed counter.

This, however, will not work in the following case:

===============================================================================

CPU0 CPU1
//taskA //taskB
preempt_disable() preempt_disable()

printk()
console_trylock()
console_unlock()
printing_task = taskA
up()
printk()
console_trylock()
console_unlock()
printing_task = taskB
^^^ reset offloading control
up()
printk()
console_trylock()
console_unlock()
printing_task = taskA
^^^ reset offloading control
up()
printk()
console_trylock()
console_unlock()
printing_task = taskB
^^^ reset offloading control
up()
...
===============================================================================

So this printk ping-pong confuses our offloading control logic.
Move it to per-CPU area and have a separate offloading control
on every CPU.

Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
---
kernel/printk/printk.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2a12d4c02da1..2f9697c71cf1 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -560,12 +560,12 @@ static inline int offloading_threshold(void)
* amount of time a process can print from console_unlock().
*
* This function must be called from 'printk_safe' context under
- * console_sem lock.
+ * console_sem lock with preemption disabled.
*/
static inline bool should_handoff_printing(u64 printing_start_ts)
{
+ static DEFINE_PER_CPU(u64, printing_elapsed);
static struct task_struct *printing_task;
- static u64 printing_elapsed;
u64 now = local_clock();
bool emergency = !printk_offloading_enabled();

@@ -578,19 +578,26 @@ static inline bool should_handoff_printing(u64 printing_start_ts)

/* A new task - reset the counters. */
if (printing_task != current) {
+ __this_cpu_write(printing_elapsed, 0);
printing_task = current;
- printing_elapsed = 0;
return false;
}

- if (time_after_eq64(now, printing_start_ts))
- printing_elapsed += now - printing_start_ts;
+ if (time_after_eq64(now, printing_start_ts)) {
+ u64 t = __this_cpu_read(printing_elapsed);
+
+ t += now - printing_start_ts;
+ __this_cpu_write(printing_elapsed, t);
+ }

/* Shrink down to seconds and check the offloading threshold */
- if ((printing_elapsed >> 30LL) < offloading_threshold())
+ if ((__this_cpu_read(printing_elapsed) >> 30LL) <
+ offloading_threshold())
return false;

if (current == printk_kthread) {
+ unsigned int cpu;
+
/*
* All tasks must offload - we don't want to keep console_sem
* locked for too long. However, printk_kthread may be the
@@ -603,7 +610,8 @@ static inline bool should_handoff_printing(u64 printing_start_ts)
* console_unlock(), it will have another full
* `offloading_threshold()' time slice.
*/
- printing_elapsed = 0;
+ for_each_possible_cpu(cpu)
+ per_cpu(printing_elapsed, cpu) = 0;
return true;
}

--
2.15.1