Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

50107
469
422682


[리눅스커널] 스케줄링: __schedule() 함수와 'bool preempt' 인자 10. 프로세스 스케줄링

다음과 같이 __schedule() 함수의 구현부를 보면 'bool preempt' 인자를 전달한다.

https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/core.c 
static void __sched notrace __schedule(bool preempt)
{
    struct task_struct *prev, *next;
    unsigned long *switch_count;
    struct rq_flags rf;

이번 시간에는 이 함수에 'bool preempt' 인자가 추가된 이력을 확인해보자.

__schedule() 함수에 'bool preempt' 인자가 추가된 패치

출처는 다음과 같다.
https://lkml.org/lkml/2015/9/30/100

패치 코드의 내용은 다음과 같다.

From fc13aebab7d8f0d19d557c721a0f25cdf7ae905c Mon Sep 17 00:00:00 2001
From: Peter Zijlstra <peterz@infradead.org>
Date: Mon, 28 Sep 2015 18:05:34 +0200
Subject: [PATCH 479/867] sched/core: Add preempt argument to __schedule()

There is only a single PREEMPT_ACTIVE use in the regular __schedule()
path and that is to circumvent the task->state check. Since the code
setting PREEMPT_ACTIVE is the immediate caller of __schedule() we can
replace this with a function argument.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <fweisbec@gmail.com>
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 kernel/sched/core.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 8d8722b..0a71f89 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3056,7 +3056,7 @@ pick_next_task(struct rq *rq, struct task_struct *prev)
  *
  * WARNING: must be called with preemption disabled!
  */
-static void __sched __schedule(void)
+static void __sched __schedule(bool preempt)
 {
    struct task_struct *prev, *next;
    unsigned long *switch_count;
@@ -3096,7 +3096,7 @@ static void __sched __schedule(void)
    rq->clock_skip_update <<= 1; /* promote REQ to ACT */

    switch_count = &prev->nivcsw;
-   if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
+   if (!preempt && prev->state) {
        if (unlikely(signal_pending_state(prev->state, prev))) {
            prev->state = TASK_RUNNING;
        } else {
@@ -3161,7 +3161,7 @@ asmlinkage __visible void __sched schedule(void)
    sched_submit_work(tsk);
    do {
        preempt_disable();
-       __schedule();
+       __schedule(false);
        sched_preempt_enable_no_resched();
    } while (need_resched());
 }
@@ -3202,7 +3202,7 @@ static void __sched notrace preempt_schedule_common(void)
 {
    do {
        preempt_active_enter();
-       __schedule();
+       __schedule(true);
        preempt_active_exit();

        /*
@@ -3267,7 +3267,7 @@ asmlinkage __visible void __sched notrace preempt_schedule_notrace(void)
         * an infinite recursion.
         */
        prev_ctx = exception_enter();
-       __schedule();
+       __schedule(true);
        exception_exit(prev_ctx);

        barrier();
@@ -3296,7 +3296,7 @@ asmlinkage __visible void __sched preempt_schedule_irq(void)
    do {
        preempt_active_enter();
        local_irq_enable();
-       __schedule();
+       __schedule(true);
        local_irq_disable();
        preempt_active_exit();
    } while (need_resched());
--
2.6.2

코드를 보면 알 수 있듯이 원래 __schedule() 함수는 인자가 없었다.
이 패치에서는  __schedule() 함수에 bool preempt 이란 인자를 추가하고 다음과 같은 패턴으로 함수를 호출한다.

   * __schedule(true);
   * __schedule(false);


패치 코드를 유심히 보면 __schedule(true) 함수를 호출하기 전에는 preempt_active_enter() 함수를 호출한다.
다음은 preempt_schedule_common() 함수이다.

@@ -3202,7 +3202,7 @@ static void __sched notrace preempt_schedule_common(void)
 {
    do {
        preempt_active_enter(); //<<-- 여기
-       __schedule();
+       __schedule(true);
        preempt_active_exit();

이번에는 preempt_schedule_irq() 함수이다. 역시 preempt_active_enter() 함수를 호출한다.

@@ -3296,7 +3296,7 @@ asmlinkage __visible void __sched preempt_schedule_irq(void)
    do {
        preempt_active_enter();  //<<-- 여기
        local_irq_enable();
-       __schedule();
+       __schedule(true);


__schedule() 함수에서 가장 먼저 변경된 코드는 다음과 같다.

+static void __sched __schedule(bool preempt)
 {
    struct task_struct *prev, *next;
    unsigned long *switch_count;
@@ -3096,7 +3096,7 @@ static void __sched __schedule(void)
    rq->clock_skip_update <<= 1; /* promote REQ to ACT */

    switch_count = &prev->nivcsw;
-   if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
+   if (!preempt && prev->state) {

if문에서 'preempt_count() & PREEMPT_ACTIVE' 구문을 삭제한 것이다.
preempt_count() 함수는 프로세스의 thread_info 구조체의 preempt_count 필드를 나타내는데 이 값을 PREEMPT_ACTIVE 플래그와 AND 비트 연산을 하는 코드였다.

이 사실로 preempt_active_enter() 함수는 프로세스의 thread_info 구조체의 preempt_count 필드에  PREEMPT_ACTIVE 플래그를 설정한다고 유추할 수 있다.
그런데 지금  preempt_active_enter() 함수는 소스 트리에서 볼 수 없다.

이력을 조금 조사해볼까?

이번에도 'Peter Zijlstra' 님이 삭제했다. preempt_active_enter() 함수를 쓰기 싫었나 보다.
preempt_active_enter() 함수가 반환하는 값을 테스트하기 싫었나? '테스트를 하기 싫어서 함수 자체를 없애 버린다!' 

From 3d8f74dd4ca1da8a1a464bbafcf679e40c2fc10f Mon Sep 17 00:00:00 2001
From: Peter Zijlstra <peterz@infradead.org>
Date: Mon, 28 Sep 2015 18:09:19 +0200
Subject: [PATCH 481/867] sched/core: Stop setting PREEMPT_ACTIVE

Now that nothing tests for PREEMPT_ACTIVE anymore, stop setting it.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
Reviewed-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 kernel/sched/core.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index cfad7f5..6344d82 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3201,9 +3201,9 @@ void __sched schedule_preempt_disabled(void)
 static void __sched notrace preempt_schedule_common(void)
 {
    do {
-       preempt_active_enter();
+       preempt_disable();
        __schedule(true);
-       preempt_active_exit();
+       sched_preempt_enable_no_resched();

        /*
         * Check again in case we missed a preemption opportunity
@@ -3254,13 +3254,7 @@ asmlinkage __visible void __sched notrace preempt_schedule_notrace(void)
        return;

    do {
-       /*
-        * Use raw __prempt_count() ops that don't call function.
-        * We can't call functions before disabling preemption which
-        * disarm preemption tracing recursions.
-        */
-       __preempt_count_add(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET);
-       barrier();
+       preempt_disable_notrace();
        /*
         * Needs preempt disabled in case user_exit() is traced
         * and the tracer calls preempt_enable_notrace() causing
@@ -3270,8 +3264,7 @@ asmlinkage __visible void __sched notrace preempt_schedule_notrace(void)
        __schedule(true);
        exception_exit(prev_ctx);

-       barrier();
-       __preempt_count_sub(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET);
+       preempt_enable_no_resched_notrace();
    } while (need_resched());
 }
 EXPORT_SYMBOL_GPL(preempt_schedule_notrace);
@@ -3294,11 +3287,11 @@ asmlinkage __visible void __sched preempt_schedule_irq(void)
    prev_state = exception_enter();

    do {
-       preempt_active_enter();
+       preempt_disable();
        local_irq_enable();
        __schedule(true);
        local_irq_disable();
-       preempt_active_exit();
+       sched_preempt_enable_no_resched();
    } while (need_resched());

    exception_exit(prev_state);
--
2.6.2

preempt_active_enter() 함수는 뭔가 선점을 활성화하는 플래그를 설정했던 것 같다.

preempt_count_add() 매크로 함수의 구현부

이번에는 preempt_active_enter() 함수의 구현부를 볼까? 2015년도에 존재했던 함수이다.
https://lkml.org/lkml/2015/5/11/519

+#define preempt_active_enter() \
+do { \
+ preempt_count_add(PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET); \
+ barrier(); \
+} while (0) 

내 예상이 맞았다. preempt_count_add() 함수를 사용해 thread_info 구조체의 preempt_count에 'PREEMPT_ACTIVE + PREEMPT_DISABLE_OFFSET' 를 더한다.

정리

음 쓸때 없는 코드를 분석하는 것 같은데, 좀 정리를 좀 하자.

   * 선점 스케줄링으로 프로세스를 CPU에서 빼버릴 때는 __schedule(true); 함수를 호출한다. 
   * __schedule(true) 함수를 호출해 선점 스케줄링을 하면 대상 프로세스는 런큐에서 제거되지는 않는다.
   * 원래 preempt_active_enter() 매크로 함수를 사용해 해당 프로세스가 선점될 대상이라고 지정했다.
   * preempt_active_enter() 함수를 제거하고 __schedule() 함수에 'bool preempt' 이란 인자를 추가했다.
   
가끔 가만히 있는 소스 코드를 분석하는 것 보다 코드가 수정된 이력을 보면 덜 지루한 것 같다.

덧글

댓글 입력 영역