다음과 같이 __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' 이란 인자를 추가했다.
가끔 가만히 있는 소스 코드를 분석하는 것 보다 코드가 수정된 이력을 보면 덜 지루한 것 같다.
최근 덧글