프로세스의 thread_info 구조체의 preempt_count 필드를 1만큼 증가시키는 함수
- preempt_disable()/preempt_enable() 함수
언제 Preemptive scheduling을 비활성화해야 할까?
- 특정 루틴에서 Preemption이 유발되면 오동작할 때
- 하드웨어 디바이스에 정확한 딜레이를 인가해야 하는 경우
- 코드 구간의 실행 시간을 정확히 지켜야 하는 경우
preemption 비활성화 주의 사항1
- preempt_disable() 함수를 호출한 다음 스케줄링 동작을 수행하는 함수를 쓰면 리눅스
시스템이 오동작
01 void configure_something_driver(void)
02 {
03 preempt_disable();
04
05 do_something();
06 msleep(100); // schedule();
07 do_something();
08
09 preempt_enable();
10 }
01 void configure_something_driver(void)
02 {
03 preempt_disable();
04 do_something();
06 preempt_enable();
07
08 mdelay(100);
09 }
preemption 비활성화 주의 사항1
- 스핀락 관련 함수에서 이미 preempt_disable() 함수를 실행
- spin_lock() 함수를 호출한 다음에 preempt_disable() 함수를 호출할 필요가 없음
https://elixir.bootlin.com/linux/v5.15.30/source/include/linux/spinlock_api_smp.h
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
preempt_disable(); // 호출하기에 또 호출할 필요가 없다
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
예시
- sock_prot_inuse_add() 함수 실행 구간 preemption 비활성화
- thread_info의 preempt_count값이 1만큼 증가
https://elixir.bootlin.com/linux/v5.10.50/source/net/packet/af_packet.c
static int packet_release(struct socket *sock)
{
...
preempt_disable();
sock_prot_inuse_add(net, sk->sk_prot, -1); // 인터럽트 유발 => 서비스 루틴 실행 마무리 => 프리엠션 실행이 되지 않는다
preempt_enable(); // 프리엠션 활성화
예시2
- smp_call_function_many() 함수 실행 구간 preemption 비활성화
https://elixir.bootlin.com/linux/v5.10.50/source/kernel/smp.c
void smp_call_function(smp_call_func_t func, void *info, int wait)
{
preempt_disable();
smp_call_function_many(cpu_online_mask, func, info, wait);
preempt_enable();
}
라즈비안 커널에서 preemption 관련 커널 컨피그
- 소스 코드 분석하기 전에 커널 컨피그를 체크 요망
.config
- #CONFIG_PREEMPT_NONE is not set
- #CONFIG_PREEMPT_VOLUNTARY is not set
- CONFIG_PREEMPT=y
- CONFIG_PREEMPT_COUNT=y
- CONFIG_PREEMPTION=y
preempt_disable() 함수
- thread_info 구조체 preempt_count를 1만큼 증가
https://elixir.bootlin.com/linux/v5.15.50/source/include/linux/preempt.h
#define preempt_disable() \
do { \
preempt_count_inc(); \ // 프리엠트 카운트 애드로 치환
barrier(); \ // 명령어 실행순서를 지키도록 함
} while (0)
#define preempt_count_inc() preempt_count_add(1)
#define preempt_count_add(val) __preempt_count_add(val) // 프리엠트 카운트 값을 1만큼 증가
preempt_enable() 함수
- thread_info의 preempt_count를 1만큼 감소
- thread_info의 preempt_count가 0이면 Preemption 실행
https://elixir.bootlin.com/linux/v5.15.30/source/include/linux/preempt.h
#define preempt_enable() \
do { \
barrier(); \
if (unlikely(preempt_count_dec_and_test())) \
__preempt_schedule(); \
} while (0)
#define preempt_count_dec_and_test() \
({ preempt_count_sub(1); should_resched(0); })
#define __preempt_schedule() preempt_schedule()
preempt_schedule() 함수
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/sched/core.c
asmlinkage __visible void __sched notrace preempt_schedule(void)
{
if (likely(!preemptible()))
return;
preempt_schedule_common();
}